(function() {

  // Independent Functions
  var $ = function(id) {
    return document.getElementById(id);
  };

  var $C = function(tagName) {
    return document.createElement(tagName);
  };

  var $T = function(text) {
    return document.createTextNode(text);
  };

  var NullFunc = function() {};

  var getPoint = function(point) {
    return point.lat().toFixed(9) + "," + point.lng().toFixed(9);
  };

  var failureList = [];

  var dirPolyCount = 0;
  var dirTimer = null;
  var MAX_TIME = 20;

  var GetDirPoly = function(query, callback) {
    dirPolyCount = 0;
    var ret = false;
    var clearTimer = function() {
      if (dirTimer) {
        clearTimeout(dirTimer);
        dirTimer = null;
      }
    }
    clearTimer();
    var dir = new GDirections();
    var onDirSuccess = function() {
      if (ret) {
        return;
      }
      clearTimer();
      var pol = this.getPolyline();
      ret = true;
      if (callback) callback(pol);
    };

    var onDirFailure = function() {
      if (ret) {
        return;
      }
      clearTimer();
      GLog.write("on Failure of the dir");
      if (dirPolyCount < MAX_TIME) {
        dirTimer = setTimeout(onDirTimeOut, 10000 * (dirPolyCount + 1));
        dirPolyCount++;
        return;
      }
      var error = dir.getStatus();
      if(error.code == 620) {
      }
      ret = true;
      if (callback) callback(null, error);
    };

    var onDirTimeOut = function() {
      if (dirPolyCount > MAX_TIME) {
        onDirFailure();
        return;
      }
      dir.load(query, {
        getPolyline: true
      });
      GLog.write(" Re Dir on Failure of the dir");
      clearTimer();
      dirTimer = setTimeout(onDirTimeOut, 10000 * (dirPolyCount + 1));
      dirPolyCount++;
    }

    dirTimer = setTimeout(onDirTimeOut, 10000);
    GEvent.addListener(dir, 'load', onDirSuccess);
    GEvent.addListener(dir, 'error', onDirFailure);

    dir.load(query, {
      getPolyline: true
    });
  }

  var HTML = {};

  HTML.Select = function(vals, names, def, idx) {
    var select = document.createElement('select');
    this.dom = select;
    if (vals.length != names.length) return select;
    for (var i = 0; i < vals.length; i++) {
      select.options[select.options.length] = new Option(names[i], vals[i], def == vals[i], def == vals[i]);
    }
    select.selectedIndex = idx || 0;
    return select;
  };

  HTML.Select.prototype.add = function(val, name, def) {
    this.dom.options[this.dom.options.length] = new Option(name, val, def == val, def == val);
  };

  HTML.Select.prototype.remove = function(idx) {
    this.dom.options.remove(idx);
  };

  HTML.Select.prototype.set = function(idx, val, name) {
    this.dom.options[idx].value = val;
    this.dom.options[idx].text = name;
  };

  var GTFS = function() {};

  GTFS.Toolbar = {};

  GTFS.mousePoint = null;

  GTFS.State = {
    selected: null,
    resolving: false,
    tripToolbar: null,
    end: true
  };

  var cbFunc = function(cb, cbe) {
    var self = this;
    return function(isSuccess, data) {
      if (!isSuccess) {
        if (cbe) cbe.call(self, data);
        return;
      }
      cb.call(self, data);
    };
  };

  GTFS.Fetcher = function() {};

  GTFS.Fetcher.instance = null;
  GTFS.Fetcher.getInstance = function() {
    if (!GTFS.Fetcher.instance) {
      GTFS.Fetcher.instance = new GTFS.Fetcher();
    }
    return GTFS.Fetcher.instance;
  };

  GTFS.Fetcher.prototype.cbFunc = function(self, callback) {
    return function(data, responseCode) {
      if (responseCode != 200) {
        if (callback) callback.call(self, data);
        return;
      }
      data = eval(data);
      if (callback) callback.call(self, data);
    };
  };

  GTFS.Fetcher.prototype.getAgency = function(self, callback) {
    var url = "/json/agency";
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  GTFS.Fetcher.prototype.getRoute = function(self, agencyId, callback) {
    var url = "/json/routesbyaid?aid=" + agencyId;
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  GTFS.Fetcher.prototype.getTrips = function(self, routeId, callback) {
    var url = "/json/tripsbyrid?rid=" + routeId;
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  GTFS.Fetcher.prototype.getStops = function(self, tripId, callback) {
    var url = "/json/stopsbytid?tid=" + tripId;
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  GTFS.Fetcher.prototype.getShapeExist = function(self, tripId, callback) {
    var url = "/json/shapeexistbytid?tid=" + encodeURIComponent(tripId);
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  GTFS.Fetcher.prototype.getShape = function(self, tripId, callback) {
    var url = "/json/shapesbytid?tid=" + encodeURIComponent(tripId);
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  GTFS.Fetcher.prototype.getRoutes = function(self, callback) {
    var url = "/json/routes";
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  GTFS.Fetcher.prototype.getPatterns = function(self, routeId, callback) {
    var url = "/json/routepatterns?route=" + encodeURIComponent(routeId);
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  GTFS.Fetcher.prototype.getStopTime = function(self, tripId, callback) {
    var url = "/json/tripstoptimes?trip=" + encodeURIComponent(tripId);
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  GTFS.Fetcher.prototype.getOriginalShape = function(self, tripId, callback) {
    var url = "/json/triporiginshape?trip=" + encodeURIComponent(tripId);
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  GTFS.Fetcher.prototype.getSavedShape = function(self, tripId, callback) {
    var url = "/json/shapes?trip=" + encodeURIComponent(tripId);
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  GTFS.Fetcher.prototype.saveShape = function(self, postBody, callback) {
    var url = "/post/shapes";
    GDownloadUrl(url, this.cbFunc(self, callback), postBody);
  };

  GTFS.Fetcher.prototype.toStation = function(self, tripId, stopId, sequence, callback) {
    var url = "/json/tostation?sid=" + encodeURIComponent(stopId) + "&tid=" + encodeURIComponent(tripId) + "&seq=" + sequence;
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  GTFS.Fetcher.prototype.updateLatLng = function(self, id, latlng, routeid, callback) {
    var url = "/json/setstoplocation?id=" + encodeURIComponent(id) + "&lat=" + encodeURIComponent(latlng.lat().toFixed(9)) + "&lng=" + encodeURIComponent(latlng.lng().toFixed(9)) + "&routeid=" + routeid;
    GDownloadUrl(url, this.cbFunc(callback));
  };

  GTFS.Fetcher.prototype.updateStopLatLng = function(self, id, latlng, callback) {
    var url = "/json/stoplatlng?id=" + encodeURIComponent(id) + "&lat=" + encodeURIComponent(latlng.lat().toFixed(9)) + "&lng=" + encodeURIComponent(latlng.lng().toFixed(9));
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  GTFS.Fetcher.prototype.insertStopTime = function(self, tid, sid, seq, lat, lng, callback) {
    var url = "/json/stoptimesinsert?tid=" + encodeURIComponent(tid) + "&sid=" + encodeURIComponent(sid) + "&seq=" + encodeURIComponent(seq) + "&lat=" + encodeURIComponent(lat.toFixed(9)) + "&lng=" + encodeURIComponent(lng.toFixed(9));
    GDownloadUrl(url, this.cbFunc(self, callback));
  };


  GTFS.Fetcher.prototype.insertOldStop = function(self, id, rid, sid, seq, callback) {
    var url = "/json/oldstopinsertbyshape?id=" + encodeURIComponent(id) + "&rid=" + encodeURIComponent(rid) + "&sid=" + encodeURIComponent(sid) + "&seq=" + encodeURIComponent(seq);
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  GTFS.Fetcher.prototype.removeStopTime = function(self, tid, sid, seq, callback) {
    var url = "/json/stoptimesremove?tid=" + encodeURIComponent(tid) + "&sid=" + encodeURIComponent(sid) + "&seq=" + encodeURIComponent(seq);
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  GTFS.Fetcher.prototype.insertStop = function(self, id, latlng, callback) {
    var url = "/json/stoplatlng?id=" + encodeURIComponent(id) + "&lat=" + encodeURIComponent(latlng.lat().toFixed(9)) + "&lng=" + encodeURIComponent(latlng.lng().toFixed(9));
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  //new functions for grouping
  GTFS.Fetcher.prototype.insertStopByShape = function(self, id, rid, sid, seq, lat, lng, callback) {
    var url = "/json/stopinsertbyshape?id=" + encodeURIComponent(id) + "&rid=" + encodeURIComponent(rid) + "&sid=" + encodeURIComponent(sid) + "&seq=" + encodeURIComponent(seq) + "&lat=" + encodeURIComponent(lat.toFixed(9)) + "&lng=" + encodeURIComponent(lng.toFixed(9));
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  GTFS.Fetcher.prototype.removeStopByShape = function(self, id, rid, sid, seq, callback) {
    var url = "/json/stopremovebyshape?id=" + encodeURIComponent(id) + "&rid=" + encodeURIComponent(rid) + "&sid=" + encodeURIComponent(sid) + "&seq=" + encodeURIComponent(seq);
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  GTFS.Fetcher.prototype.replaceStopByShape = function(self, id, rid, sid, nid, seq, callback) {
    var url = "/json/stopreplacebyshape?id=" + encodeURIComponent(id) + "&rid=" + encodeURIComponent(rid) + "&sid=" + encodeURIComponent(sid) + "&nid=" + encodeURIComponent(nid) + "&seq=" + encodeURIComponent(seq);
    GDownloadUrl(url, this.cbFunc(self, callback));
  };

  GTFS.Fetcher.prototype.toStationByShape = function(self, id, rid, sid, seq, callback) {
    var url = "/json/tostationbyshape?id=" + encodeURIComponent(id) + "&rid=" + encodeURIComponent(rid) + "&sid=" + encodeURIComponent(sid) + "&seq=" + encodeURIComponent(seq);
    GDownloadUrl(url, this.cbFunc(self, callback));
  };



  GTFS.Fetcher.prototype.copyStop = function(self, id, callback) {
    var url = "/json/stopcopy?id=" + encodeURIComponent(id);
    GDownloadUrl(url, this.cbFunc(self, callback));
  };


  GTFS.Fetcher.prototype.genGTFS = function(self, callback) {
    var url = "/json/getgtfsfeed/";
    GDownloadUrl(url, this.cbFunc(self, callback));
  };


  GTFS.DataBase = function() {};

  GTFS.DataBase.prototype.TYPE = 'DataBase';

  GTFS.DataBase.prototype.cbFunc = cbFunc;

  GTFS.DataBase.prototype.getType = function() {
    return this.TYPE;
  };

  GTFS.Data = function() {
    this.TYPE = 'Data';
    this.retrieved = false;
    this.childs = [];
    this.changed = false;
    this.observers = {};
  };

  GTFS.Data.prototype = new GTFS.DataBase();

  GTFS.Data.prototype.fetcher = GTFS.Fetcher.getInstance();

  GTFS.Data.prototype.addListener = function(obj, event, func) {
    if (!this.observers[event]) {
      this.observers[event] = [];
    }
    var handler = {
      e: event,
      o: obj,
      f: func
    };
    this.observers[event].push(handler);
  };

  GTFS.Data.prototype.removeListener = function(o) {
    if (!this.observers[o.event] || ! this.observers[o.event].length) return false;
    for (var i = 0; i < this.observers[o.event].length; i++) {
      if (o == this.observers[o.event][i]) {
        this.observers[o.event].splice(i, 1);
        return true;
      }
    }
    return false;
  };

  GTFS.Data.prototype.onEvent = function(e, data) {
    if (!this.observers[e] || ! this.observers[e].length) return false;
    for (var i = 0; i < this.observers[e].length; i++) {
      var o = this.observers[e][i];
      o.f.call(o.o, data);
    }
    return true;
  };

  GTFS.Data.prototype.add = function(data) {
    for (var i = 0; i < this.childs.length; i++) {
      if (data == this.childs[i]) return i;
    }
    this.childs.push(data);
    return i;
  };

  GTFS.Data.prototype.remove = function(data) {
    for (var i = 0; i < this.childs.length; i++) {
      if (data == this.childs[i]) {
        this.childs.splice(i, 1);
        return true;
      }
    }
    return false;
  };

  GTFS.Data.prototype.save = function() {
    this.saveMe();
    for (var i = 0; i < this.childs.length; i++) {
      this.save();
    }
  };

  GTFS.Data.icons = {};

  GTFS.Data.Route = function() {
    this.TYPE = 'Route';
    this.childs = [];
    this.id = null;
    this.shortName = null;
    this.longName = null;
    this.type = null;
    this.agencyId = null;
    this.desc = null;
    this.url = null;
    this.color = null;
    this.textColor = null;
  };

  GTFS.Data.Trip = function() {
    this.TYPE = 'Trip';
    this.childs = [];
    this.id = null;
    this.headsign = null;
    this.routeId = null;
    this.serviceId = null;
    this.directionId = null;
    this.blockId = null;
    this.shapeId = null;
    this.curShape = null;
    this.savedShape = null;
    this.dirShape = null;
    this.oriShape = null;
  };

  GTFS.Data.Shape = function() {
    this.TYPE = 'Shape';
    this.childs = [];
    this.id = null;
    this.lat = null;
    this.lng = null;
    this.idx = null;
    this.dist = null;
  };

  GTFS.Data.Leg = function() {
    this.TYPE = 'Leg';
    this.childs = [];
    this.color = null;
    this.enable = null;
    this.preStop = null;
    this.sufStop = null;
    this.polyline = null;
  };

  GTFS.Data.Stop = function() {
    this.TYPE = 'Stop';
    this.childs = [];
    this.id = null;
    this.name = null;
    this.lat = null;
    this.lng = null;
    this.desc = null;
    this.zoneId = null;
    this.url = null;
    this.code = null;
    this.locationType = null;
    this.parentStation = null;
  };

  GTFS.Data.Agency = function() {
    this.TYPE = "Agency";
    this.childs = [];
    this.routes = [];
    this.id = null;
    this.name = null;
    this.url = null;
    this.timeZone = null;
    this.lang = null;
    this.phone = null;
  };

  GTFS.Data.StopTime = function() {
    this.TYPE = "StopTime";
    this.childs = [];
    this.tripId = null;
    this.arrivalTime = null;
    this.departureTime = null;
    this.stopId = null;
    this.stopSequence = null;
    this.stopHeadsign = null;
    this.pickupType = null;
    this.dropOffType = null;
    this.shapeDistTraveled = null;
  };

  GTFS.Data.Manager = function() {
    this.TYPE = "Manager";
    this.childs = [];
    /*
    this.toolbar = new GTFS.Toolbar.Global();
    map.addControl(this.toolbar);
    */
    this.saveAllNode = $C('span');
    this.saveAllNode.className = "ToolBarTab";
    this.saveAllNode.innerHTML = "Save";

    this.verifyAllNode = $C('span');
    //this.verifyAllNode.innerHTML = "Verify All";
    this.verifyAllNode.innerHTML = "Generate All Shapes";
    this.verifyAllNode.title = "Generate shapes for all trips";
    this.verifyAllNode.className = "ToolBarTab";

    this.genGTFSNode = $C('span');
    this.genGTFSNode.innerHTML = "Export GTFS";
    this.genGTFSNode.className = "ToolBarTab";

    this.gnote = $C('span');
    this.gnote.id = 'gnote';
    var container = $('controls')
    container.appendChild(this.saveAllNode);
    container.appendChild(this.verifyAllNode);
    container.appendChild(this.genGTFSNode);
    container.appendChild(this.gnote);    
    //var verifyAll = this.toolbar.getVerifyNode();
    GEvent.bindDom(this.verifyAllNode, 'click', this, function() {
      this.verifyAll(function() {
        this.gnote.innerHTML = "All shapes are generated!";
      });
    });

    //var saveAll = this.toolbar.getSaveNode();
    GEvent.bindDom(this.saveAllNode, 'click', this, function() {
      this.saveAll(this.onSaveEnd);
    });
    //var genGTFS = this.toolbar.getGenGTFSNode();
    GEvent.bindDom(this.genGTFSNode, 'click', this, function() {
      this.genGTFS(this.onGenEnd);
    });
  };

  GTFS.Data.ShapeManager = function() {
    this.TYPE = "ShapeManager";
    this.childs = [];
  };

  GTFS.Data.ShapeStop = function() {
    this.TYPE = "ShapeStop";
    this.childs = [];
  };



  GTFS.Data.Manager.prototype = new GTFS.Data();
  GTFS.Data.Agency.prototype = new GTFS.Data();
  GTFS.Data.Route.prototype = new GTFS.Data();
  GTFS.Data.Trip.prototype = new GTFS.Data();
  GTFS.Data.Shape.prototype = new GTFS.Data();
  GTFS.Data.Leg.prototype = new GTFS.Data();
  GTFS.Data.Stop.prototype = new GTFS.Data();
  GTFS.Data.StopTime.prototype = new GTFS.Data();

  GTFS.Data.ShapeManager.prototype = new GTFS.Data();
  GTFS.Data.ShapeStop.prototype = new GTFS.Data();

  GTFS.Data.stops = {};
  GTFS.Data.shapes = {};
  GTFS.Data.dirShapes = {};
  GTFS.Data.originShapes = {};
  GTFS.Data.verifyShapes = {};
  GTFS.Data.verifies = {};

  // Manager member functions
  GTFS.Data.Manager.prototype.onSaveEnd = function(){
	    var note = document.getElementById('gnote');
	note.innerHTML = "Save Succeeded";

  }
  GTFS.Data.Manager.prototype.onGenEnd = function(data){
	    var note = document.getElementById('gnote');
		note.innerHTML = "Export Succeeded";
  }
  GTFS.Data.Manager.prototype.genGTFS = function(callback){
	    var note = document.getElementById('gnote');
		note.innerHTML = "Generating the GTFS File"; 
    var self = this;
    this.fetcher.genGTFS(this, function(){
      if(callback) callback();
    });
  }

  GTFS.Data.Manager.prototype.getAgency = function(callback) {
    var self = this;
    this.fetcher.getAgency(this, function(data) {
        var agency = new GTFS.Data.Agency();
        agency.id = "0";
        agency.name = "All";
        agency.url = "";
        agency.phone = "";
        agency.timezone = "";
        agency.lang = "";
        self.childs.push(agency);          	
      for (var i = 0; i < data.length; i++) {
        var tmp = data[i];
        var agency = new GTFS.Data.Agency();
        agency.id = tmp.id;
        agency.name = tmp.name;
        agency.url = tmp.url;
        agency.phone = tmp.phone;
        agency.timezone = tmp.timezone;
        agency.lang = tmp.lang;
        self.childs.push(agency);
      }

      callback();
    });
  };

  GTFS.Data.Manager.prototype.showAgency = function() {
    var container = $('agencies');
    var vals = [];
    var names = [];
    for (var i = 0; i < this.childs.length; i++) {
      var agency = this.childs[i];
      vals.push(agency.id);
      names.push(agency.name);
    }

    if (!vals.length) return;
    var select = HTML.Select(vals, names, vals[0]);
    container.appendChild(select);
    this.select = select;
    GEvent.bindDom(select, 'change', this, this.onSelect);
    this.onSelect();
  };

  GTFS.Data.Manager.prototype.onSelect = function() {
    // var option = this.select.options[this.select.selectedIndex];
    var idx = this.select.selectedIndex || 0;
    var agency = this.childs[idx];
    var self = this;
    agency.getRoute(function() {
      agency.showRoute();
    });
  };

  GTFS.Data.Manager.prototype.verifyAll = function(callback) {
    if (this.inAsyncAll) return;
    this.inAsyncAll = true;
    if (this.verified) {
      if (callback) callback();
      return;
    }
    var self = this;
    var idx = this.select.selectedIndex || 0;
    var agency = this.childs[idx];
    agency.verifyAll(callback);
  };

  GTFS.Data.Manager.prototype.saveAll = function(callback) {
    if (this.inAsyncAll) {
      return;
    }
    this.inAsyncAll = true;
    var self = this;
    var idx = this.select.selectedIndex || 0;
    var agency = this.childs[idx];
    agency.saveAll(function(){
      if(callback) callback();
      self.inAsyncAll = false;
    });
  }

  GTFS.Data.Manager.prototype.getDefaultIcon = function() {
    var icon = new GIcon();
    icon.iconSize = new GSize(12, 20);
    icon.shadowSize = new GSize(22, 20);
    icon.iconAnchor = new GPoint(6, 20);
    icon.infoWindowAnchor = new GPoint(5, 1);
    return icon;
  };

  GTFS.Data.Manager.prototype.initIcon = function() {
    GTFS.Data.icons.normal = this.getDefaultIcon();
    GTFS.Data.icons.normal.image = "/file/mm_20_yellow.png";
    GTFS.Data.icons.normal.shadow = "/file/mm_20_shadow.png";

    GTFS.Data.icons.edit = this.getDefaultIcon();
    GTFS.Data.icons.edit.image = "/file/mm_20_blue_trans.png";
    GTFS.Data.icons.edit.shadow = "/file/mm_20_shadow_trans.png";

    GTFS.Data.icons.saved = this.getDefaultIcon();
    GTFS.Data.icons.saved.image = "/file/mm_20_red_trans.png";
    GTFS.Data.icons.saved.shadow = "/file/mm_20_shadow_trans.png";
  };

  GTFS.Data.Trip.colors = {
    def: '#0000FF',
    saved: '#FF0000',
    programmed: '#00FF00',
    original: '#8552a1',
    modified: '#7fb80e',
    sufDir: '#f15b6c',
    preDir: '#1d953f'
  };

  // Agency member functions
  GTFS.Data.Agency.prototype.verifyAll = function(callback) {
    if (this.inAsyncAll) return;
    this.inAsyncAll = true;
    if (this.verified) {
      if (callback) callback();
      return;
    }
    var idx = 0;
    var self = this;
    var onChild = function(isSuc) {
      if (idx == self.childs.length) {
        self.inAsyncAll = false;
        self.verified = isSuc;
        if (callback) callback();
        return;
      }
      var child = self.childs[idx];
      child.verifyAll(function(isSuc) {
        idx++;
        onChild(isSuc);
      });
    };
    onChild();
  };

  GTFS.Data.Agency.prototype.saveAll = function(callback) {
    if (this.inAsyncAll) return;
    this.inAsyncAll = true;
    var idx = 0;
    var self = this;
    var onChild = function(isSuc) {
      if (idx == self.childs.length) {
        self.inAsyncAll = false;
        self.verified = isSuc;
        if (callback) callback();
        return;
      }
      var child = self.childs[idx];
      child.saveAll(function(isSuc) {
        idx++;
        onChild(isSuc);
      });
    };
    onChild();
  };



  GTFS.Data.Agency.prototype.getRoute = function(callback) {
    var self = this;
    if (this.retrieved) {
      if (callback) callback();
      return;
    }
    this.fetcher.getRoute(this, this.id, function(data) {
      for (var i = 0; i < data.length; i++) {
        var tmp = data[i];
        var route = new GTFS.Data.Route();
        for (var o in tmp) {
          route[o] = tmp[o];
        }
        route['name'] = route['longName'] || route['shortName'];
        route.addListener(self, 'click', self.onRouteSelect);
        self.childs.push(route);
      }
      self.childs.sort(function(a, b){
    	  var ret = a.name > b.name; 
    	  return ret;
      })
      self.retrieved = true;
      if (callback) callback();
    });
  };

  GTFS.Data.curRoute = null;

  GTFS.Data.Agency.prototype.onRouteSelect = function(data) {
    if (!this.selectedRoute) {
      this.selectedRoute = data.route;
      GTFS.Data.curRoute = data.route;
      return;
    }
    if (this.selectedRoute != data.route) {
      this.selectedRoute.reset();
      this.selectedRoute = data.route;
      GTFS.Data.curRoute = data.route;
    }
  };

  GTFS.Data.Agency.prototype.showRoute = function() {
    var dom = $('routeList');
    dom.innerHTML = "";
    for (var i = 0; i < this.childs.length; i++) {
      this.childs[i].show(dom);
    }
  };

  // Route member functions
  GTFS.Data.Route.prototype.getTrips = function(callback, notShow) {
    if (!this.childs.length) {
      this.fetcher.getTrips(this, this.id, (function(data) {
        var trips = [];
        var shapes = {};
        var shapeIds = [];
        for (var i = 0; i < data.length; i++) {
          var trip = new GTFS.Data.Trip();
          for (var o in data[i]) {
            trip[o] = data[i][o];
          }
          if (!shapes[trip.shapeId]) {
            shapes[trip.shapeId] = [];
            shapeIds.push(trip.shapeId);
          }
          shapes[trip.shapeId].push(trip);
          // trip.setDom(this.tripSection);
          trip.setRoute(this);
          trips.push(trip);
          trip.rid = this.id;
          this.childs.push(trip);
        }
        this.trips = trips;
        this.shapes = shapes;
        this.shapeIds = shapeIds;
        this.initShapeTrips(this.tripSection, notShow);
        if(GTFS.Data.curRoute != this){
          if(notShow) {
            this.reset();
          }
        }

        if (callback) callback();
      }));
    } else {
      if (callback) callback();
    }
  };

  GTFS.Data.Route.prototype.initShapeTrips = function() {
    for (var shapeId in this.shapes) {
      var shapeDom = $C("div");
      var shapeTitle = $C("p");
      shapeTitle.className = "gtfs-shape";
      shapeDom.appendChild(shapeTitle);

      shapeTitle.innerHTML = "Shape:" + shapeId;      
      if(this.shapes[shapeId]) {
    	  var shapeDesc = $C('span');
    	  shapeDesc.innerHTML = this.shapes[shapeId][0].shapeDesc || "";
    	  shapeDom.appendChild(shapeDesc);    	  
      }
      
      this.tripSection.appendChild(shapeDom);
      this.initTrips(shapeId, shapeDom);
    }
  }

  GTFS.Data.Route.prototype.initTrips = function(shapeId, shapeDom) {
    var hideButton = null;
    for (var i = 0, trip; trip = this.shapes[shapeId][i]; i++) {
      if(i < 3) {
        trip.setDom(shapeDom, this.statusImgNode, shapeId);
      } else {
        if(!hideButton)
        {
          hideButton = $C('span');
          hideButton.innerHTML = "...";
          hideButton.__hide = true;
          shapeDom.appendChild(hideButton);
          shapeDom.appendChild($C('br'));
          hideButton.className = "hideButton";
          GEvent.bindDom(hideButton, 'click', hideButton, this.onSelectButton);
          hideButton.trips = [];
        }
        node = trip.setDom(shapeDom, this.statusImgNode, shapeId)
        hideButton.trips.push(node);
        node.style.display = "none";
      }
    }
  }
  GTFS.Data.Route.prototype.onSelectButton = function() {
    if(this.__hide){
      if(this.trips) {
        for(var i = 0, node; node = this.trips[i]; i++){
          node.style.display = "";
        }
      }
      this.innerHTML = "&uArr;";
      this.__hide = false;
    } else {
      if(this.trips) {
        for(var i = 0, node; node = this.trips[i]; i++){
          node.style.display = "none";
        }
      }
      this.innerHTML = "...";
      this.__hide = true;
    }
  }

  GTFS.Data.Route.prototype.onSelect = function() {
    if (GTFS.Data.Trip.inAsync) return;
    this.tripSection.style.display = "";
    this.routeNode.className = "routeChoiceSelected";
    this.onEvent("click", {
      route: this
    });
    if (GTFS.Data.Route.toolbar) {
      map.removeControl(GTFS.Data.Route.toolbar);
    }
    this.getTrips();
  };

  GTFS.Data.Route.prototype.saveAll = function(callback) {
    var self = this;
    if(!this.shapeIds || !this.shapeIds.length){
      if(callback) callback()
      return;
    }
    if(this.startRouteAsync) return;
    this.startRouteAsync = true;
    var idx = 0;
    var onChild = function(isSuc) {
      if (idx == self.shapeIds.length) {
        self.startRouteAsync = false;
        self.verified = isSuc;
        if (callback) callback();
        return;
      }
      var id = self.shapeIds[idx];
      var trip = self.shapes[id][0];
      if(trip.shape){
        trip.saveShape(function(isSuc){
          idx++;
          onChild(isSuc);
        }, true);
      } else {
          idx++;
          onChild(isSuc);
      }
    };
    onChild(false);
  };


  GTFS.Data.Route.prototype.verifyAll = function(callback) {
    failureList = [];
    var self = this;
    if (!this.childs.length) {
      this.getTrips(function() {
          self.verifyAll(callback);
        }, true);
      return;
    }
    if (this.verified) {
      if (callback) callback();
      return;
    }
    if(this.startRouteAsync) return;
    this.startRouteAsync = true;
    var idx = 0;
    var onFailureList = function(callback) {
      GLog.write("Inside recovery");
      GLog.write('idx = ' + idx);
      var trip = failureList[idx];
      if (!trip) {
        self.startRouteAsync = false;
        self.verified = true;
        GLog.write('End of recovery ' + idx);
        idx = 0;
        failureList = [];
        if (callback) callback();
        return;
      }
      trip.getStops(function() {
        trip.checkAndVerifyShape(function() {
          idx++;
          onFailureList();
        });
      }, true);
    }

    var onChild = function(isSuc) {
      if (idx == self.shapeIds.length) {
        if (failureList.length) {
          idx = 0;
          onFailureList();
          return;
        }
        self.startRouteAsync = false;
        self.verified = isSuc;
        if (callback) callback();
        return;
      }
      var id = self.shapeIds[idx];
      var trip = self.shapes[id][0];
        trip.getStops(function() {
            trip.checkAndVerifyShape(function(isExist) {
              if (!isExist) {
                  trip.getDirShape(function(isSuc){
                    if(!isSuc) {
                      failureList.push(trip);
                      GLog.write("failure list add");
                    } else {
                      trip.merge(true);
                      if (!trip.shape) {
                        trip.setShape(trip.toData(), trip.stops, true);
                      }
                      trip.saveShape(null, true);
                    }
                    GTFS.Data.verifyShapes[this.shapeId] = true;
                    onChild(true);
                  }, true, true);
              } else {
                GTFS.Data.verifyShapes[this.shapeId] = true;
                idx++;
                onChild(isSuc);
              }
            });
        }, true);
    };
    onChild(false);
  };

  GTFS.Data.Route.prototype.show = function(dom) {
    if (!this.statusImgNode) {
      this.statusImgNode = $C('img');
      this.statusImgNode.alt = "status image";
      this.statusImgNode.src = "file/loading.gif";
      this.statusImgNode.style.display = "none";

      var name = this.longName || this.shortName;
      this.shortNameNode = $C('span');
      this.shortNameNode.className = "shortName";
      this.shortNameNode.appendChild(this.statusImgNode);
      this.shortNameNode.appendChild($T(name));

      this.routeNode = $C("div");
      this.routeNode.appendChild(this.shortNameNode);
      //this.routeNode.appendChild($T(name));
      this.routeNode.id = "route_" + this.id;
      this.routeNode.className = "routeChoice";
      this.routeNode.title = name;
      GEvent.bindDom(this.routeNode, "click", this, this.onSelect);

      this.routeContainer = $C("div");
      this.routeContainer.id = "route_container_" + this.id;
      this.routeContainer.className = "routeContainer";
      this.routeContainer.appendChild(this.routeNode);

      

      this.tripSection = $C("div");
      this.tripSection.className = "tripSection";

      this.routeContainer.appendChild(this.tripSection);
    }
    dom.appendChild(this.routeContainer);
    this.routeNode.className = "routeChoice";
    this.tripSection.style.display = "";
  };

  GTFS.Data.Route.prototype.reset = function() {
    this.statusImgNode.style.display = "none";
    this.tripSection.style.display = "none";

    this.routeNode.className = "routeChoice";
  };

  // Trip member functions
  GTFS.Data.Trip.prototype.getDirShape = function(callback, force, notShow) {
    var shapeId = this.shapeId;
    var routeId = this.route.longName || this.route.shortName;
    if(GTFS.Data.dirShapes[shapeId] && !force){
      if (callback) callback(true);
    }
    
    if(GTFS.Data.dirShapes[shapeId]){
      GTFS.Data.dirShapes[shapeId].hideLegs(true);
    }

    var idx = 0;
    var self = this;
    var count = self.stops.length;
    var gnote = document.getElementById('gnote');
    var startDir = function() {
      gnote.innerHTML = "Generating Shape " + shapeId + " " + (idx + 1) + " of " + count + " in route " + routeId;
      var start = self.stops[idx];
      var end = self.stops[idx + 1];
      var query = "from: " + start.getQuery() + " to: " + end.getQuery();
      GetDirPoly(query, function(poly, error) {
        if (!poly) {
          gnote.innerHTML += " Shape generating failed with code:" + error.code;
          GLog.write('Failed to generate leg of the shape: ' + shapeId);
          if (callback) callback(false, idx);
          return;
        }
        var leg = new GTFS.Data.Leg();
        var color = GTFS.Data.Trip.colors.programmed;
        leg.setPolyline(poly, color)
        leg.setStops(start, end);
        if (!notShow) leg.show();
        end.preVerifyLeg = leg;
        start.sufVerifyLeg = leg;
        if (idx == self.stops.length - 2) {
          gnote.innerHTML = " Shape generating succeeded!";
          var shape = new GTFS.Data.Shape();
          shape.setStops(self.stops);
          GTFS.Data.dirShapes[shapeId] = shape;
          self.dirShape = shape;
          if (callback) {
            setTimeout( function() {callback(true, idx)}, 210);
          }
        } else {
          idx++;
          setTimeout(startDir, 210);
        }
      });
    };
    startDir();
  }

  GTFS.Data.Trip.prototype.verifyAll = function(callback) {
    var self = this;
    this.getStops(function() {
      self.verify(callback, false, true);
    },
    true);
  };

  GTFS.Data.Trip.prototype.setRoute = function(route) {
    this.route = route;
  }

  GTFS.Data.Trip.prototype.setDom = function(dom, indicator, shapeId) {
    this.indicator = indicator;
    this.shapeId = shapeId;
    this.parentNode = dom;
    this.node = $C('span');
    this.node.id = "trip_" + this.id;
    this.node.className = "tripChoice";
    this.node.innerHTML = this.startTime;
    dom.appendChild(this.node);
    dom.appendChild($T(" "));
    GEvent.bindDom(this.node, 'click', this, this.onSelect);
    return this.node;
  };

  GTFS.Data.Trip.prototype.onSelect = function() {
    if (GTFS.Data.Trip.inAsync) return;
    GTFS.Data.Trip.inAsync = true;
    map.clearOverlays();
    var self = this;
    if (GTFS.Data.Route.selectTrip) {
      GTFS.Data.Route.selectTrip.node.className = "tripChoice";
    }
    GTFS.Data.Route.selectTrip = this;
    this.node.className = "tripChoiceSelected";
    this.initToolbar();
    this.getStops(function() {
      self.getShape(function() {
          self.showOriginalShape(true, function(){
            GTFS.Data.Trip.inAsync = false;
            self.showCurrentShape(true);
        });
      });
    });
  };

  GTFS.Data.Trip.prototype.setStopTime = function(data) {
    if(!data || !data.length) return;
    this.stoptimes = [];
    var stopTag = false;
    if (!GTFS.Data.stops[this.shapeId]) {
      GTFS.Data.stops[this.shapeId] = [];
      stopTag = true;
    }
    this.stops = GTFS.Data.stops[this.shapeId];

    for (var i = 0; i < data.length; i++) {
      var stoptimes = new GTFS.Data.StopTime();
      stoptimes.arrival_time = data[i].atime;
      stoptimes.departure_time = data[i].dtime;
      stoptimes.trip = this;
      stoptimes.sequence = data[i].sequence * 1;
      if (stopTag) {
        var stop = new GTFS.Data.Stop();
        stop.id = data[i].sid;
        stop.lat = data[i].slat;
        stop.lng = data[i].slon;
        stop.name = data[i].sname;
        stop.parentStation = data[i].spid;
        GTFS.Data.stops[this.shapeId].push(stop);
        stop.trip = this;
        stop.shapeId = this.shapeId;
        stop.sequence = stoptimes.sequence;
        stoptimes.stop = stop;
        stop.createMarker();
        stop.initDir();

        if (i > 0) {
          stop.setPreStop(this.stops[i - 1]);
          this.stops[i - 1].setSufStop(stop);
        }

      } else {
        stoptimes.stop = GTFS.Data.stops[this.shapeId][stoptimes.sequence * 1 - 1];
      }
      this.stoptimes.push(stoptimes);
    }
  };

  GTFS.Data.Trip.prototype.toData = function() {
    var data = [];
    for (var i = 0; i < this.stops.length - 1; i++) {
      var tmp = this.stops[i].sufLeg.toData();
      for (var j = 0; j < tmp.length; j++) {
        data.push(tmp[j]);
      }
    }
    return data;
  };

  GTFS.Data.Trip.prototype.setShape = function(data, stops, notShow) {
    if (!GTFS.Data.shapes[this.shapeId]) {
      var shape = new GTFS.Data.Shape();
      shape.data = data;
      shape.id = this.shapeId;
      shape.rid = this.rid;
      shape.route = this.route;
      shape.init(stops, notShow);
      shape.history = new GTFS.History();
      GTFS.Data.shapes[this.shapeId] = shape;
    }
    this.shape = GTFS.Data.shapes[this.shapeId];
  };

  GTFS.Data.Trip.prototype.merge = function(isShow) {
    for (var i = 0; i < this.stops.length; i++) {
      var stop = this.stops[i];
      if (stop.sufVerifyLeg) {
        var leg = stop.sufVerifyLeg.clone();
        if(!stop.sufLeg){
        	stop.sufLeg = leg;
        	stop.sufLeg.setColor(GTFS.Data.Trip.colors.modified)
        } else {
          stop.sufLeg.setPolyline(leg.getPolyline(), GTFS.Data.Trip.colors.modified);
        }
        if (!isShow) stop.sufLeg.show();
      }
    }
  };

  GTFS.Data.Trip.prototype.showStopTime = function() {
    var bounds = new GLatLngBounds();

    for (var i = 0; i < this.stops.length; i++) {
      map.addOverlay(this.stops[i].getMarker());
      bounds.extend(this.stops[i].getMarker().getLatLng());
    }
    map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));
  };

  GTFS.Data.Trip.prototype.getStops = function(callback, notShow) {
    if (!this.stoptimes || !this.stoptimes.length) {
      this.fetcher.getStops(this, this.id, function(data) {
        this.setStopTime(data);
        if (!notShow) this.showStopTime();
        if(callback) callback();
      });
    } else {
      if (!notShow) this.showStopTime();
      if(callback) callback();
    }
  };

  GTFS.Data.Trip.prototype.checkAndVerifyShape = function(callback) {
    if (!GTFS.Data.verifyShapes[this.shapeId]) {
      this.fetcher.getShapeExist(this, this.id, function(data) {
        if (callback) callback(!!data[0]);
        });
    } else {
      if (callback) callback(true);
    }
  };
  GTFS.Data.Trip.prototype.getShape = function(callback, notShow) {
    if (!GTFS.Data.shapes[this.shapeId]) {
      this.fetcher.getShape(this, this.id, function(data) {
        this.setShape(data, this.stops, notShow)
        if (callback) callback();
      });
    } else {
      this.shape = GTFS.Data.shapes[this.shapeId];
      if (callback) callback();
    }
  };

  GTFS.Data.Trip.prototype.getOriginShape = function(callback) {
    if (GTFS.Data.shapes[this.shapeId]) {
      this.originShape = GTFS.Data.originShapes[this.shapeId];
    }
    
    if (!this.originShape) {
      if (this.inAsyncOrigin) return;
      this.inAsyncOrigin = true;
      this.fetcher.getOriginalShape(this, this.id, function(data) {
        var points = data;
        var latlngs = [];
        for (var i = 0; i < points.length; ++i) {
          var ll = new GLatLng(points[i][0], points[i][1]);
          latlngs.push(ll);
        }
        var color = GTFS.Data.Trip.colors.original;
        this.originShape = new GPolyline(latlngs, color, 4);
        GTFS.Data.originShapes[this.shapeId] = this.originShape;
        this.inAsyncOrigin = false;
        if (callback) callback();
      });
    }
  };

  GTFS.Data.Trip.prototype.showOriginalShape = function(show, callback) {
    if(GTFS.Data.originShapes[this.shapeId] && !this.originShape){
      this.originShape = GTFS.Data.originShapes[this.shapeId];
    }
    if (!this.originShape) {
      var self = this;
      this.getOriginShape(function() {
        self.showOriginalShape(show, callback);
      });
      return;
    }
    if (show) {
      map.addOverlay(this.originShape);
    } else {
      map.removeOverlay(this.originShape);
    }
    if (this.toolbar.shapesObjs) {
      this.toolbar.shapesObjs[GTFS_TRIPTOOLBAR_SHAPES_ORIGINAL].checked = show;
    }
    if(callback) callback();
  };
  
  GTFS.Data.Trip.prototype.initToolbar = function() {
    if (!this.toolbar) {
      this.toolbar = new GTFS.Toolbar.Trip();
    }
    if (!this.history) {
      this.history = new GTFS.History();
    }
    if (!GTFS.Data.Route.toolbar || GTFS.Data.Route.toolbar != this.toolbar) {
      if (GTFS.Data.Route.toolbar)
        map.removeControl(GTFS.Data.Route.toolbar);
      map.addControl(this.toolbar);
      GTFS.Data.Route.toolbar = this.toolbar;
      /*
      this.toolbar.registerOption(GTFS_TRIPTOOLBAR_SHAPES, this, function() {
        this.editMode = false;
        map.closeInfoWindow();
        if(this.shape)
        
      });
      */
      this.toolbar.registerOption(GTFS_TRIPTOOLBAR_EDITOR, this, function(mode) {
        this.editMode = mode;
        map.closeInfoWindow();
        if(this.shape) {
        	if(mode) {
            this.shape.enableStops();
        	} else {
            this.shape.disableStops();
        	}
        }
      });
      
      this.toolbar.registerShapes(GTFS_TRIPTOOLBAR_SHAPES_ORIGINAL, this, function(isCheck) {
        this.showOriginalShape(isCheck);
      });
      this.toolbar.registerShapes(GTFS_TRIPTOOLBAR_SHAPES_SAVED, this, function(isCheck) {
        this.showSavedShape(isCheck);
      });
      this.toolbar.registerShapes(GTFS_TRIPTOOLBAR_SHAPES_PROGRAMMED, this, function(isCheck) {
        if (isCheck) {
          this.toolbar.shapesObjs[GTFS_TRIPTOOLBAR_SHAPES_PROGRAMMED].disabled = true;
        }
        this.showProgrammedShape(isCheck);
      });

      this.toolbar.registerShapes(GTFS_TRIPTOOLBAR_SHAPES_CURRENT, this, function(isCheck) {
        this.showCurrentShape(isCheck);
      });

      this.toolbar.registerEditor(GTFS_TRIPTOOLBAR_EDITOR_VERIFY, this, function() {
        this.showProgrammedShape(true, true);
      });
      this.toolbar.registerEditor(GTFS_TRIPTOOLBAR_EDITOR_SAVE, this, function() {
    	this.merge();
        this.saveShape();
      });

      this.toolbar.registerEditor(GTFS_TRIPTOOLBAR_EDITOR_REVERT, this, function() {
        return;
        this.revert();
      });

      this.toolbar.registerEditor(GTFS_TRIPTOOLBAR_EDITOR_MERGE, this, function() {
        return;
        this.merge();
      });

      this.toolbar.registerEditor(GTFS_TRIPTOOLBAR_EDITOR_REDO, this, function() {
        return;
        this.shape.history.redo();
      });

      this.toolbar.registerEditor(GTFS_TRIPTOOLBAR_EDITOR_UNDO, this, function() {
        return;
        this.shape.history.undo();
      });

    }
  };

  GTFS.Data.Trip.prototype.revert = function() {

    // this.history.execute(new GTFS.Command.Shapes.Revert(this));
    var count = this.originalShapes.getVertexCount();
    var data = [];
    for (var i = 0; i < count; i++) {
      var latlng = this.originalShapes.getVertex(i);
      data.push({
        lat: latlng.lat(),
        lng: latlng.lng(),
        dist: 0.0
      });
    }

    this.shape.feedData(data);
    this.showCurrentShape(true);
  };

  /**
   * Save shapes to server side
   */
  GTFS.Data.Trip.prototype.saveShape = function(callback, notShow) {
    var note = $('note') || $('gnote');
    if (this.inAsyncSave) return;
    this.inAsyncSave = true;
    note.innerHTML = "saving shape " + this.shape.id + " now...";
    var postBody = "";
    var points = [];
    var latlngs = [];
    this.shape.updateData();
    var data = this.shape.data;
    var self = this;

    for (var i = 0; i < data.length; i++) {
      points.push(data[i].lat.toFixed(9) + "," + data[i].lng.toFixed(9) + ",0");
      latlngs.push(new GLatLng(data[i].lat, data[i].lng));
    }
    postBody = "tripid=" + this.id + "&points[]=" + points.join('&points[]=');
    this.fetcher.saveShape(this, postBody, (function() {
      note.innerHTML = "saved!";
      var color = GTFS.Data.Trip.colors.saved;
      if (self.savedShape) {
        map.removeOverlay(self.savedShape);
      }
      self.savedShape = new GPolyline(latlngs, color, 4);
      if (!notShow) self.showSavedShape(true);
      this.inAsyncSave = false;
      if (callback) callback();
    }));
  };

  GTFS.Data.Trip.prototype.showSavedShape = function(show) {
    if (!this.savedShape) {
      var self = this;
      this.getSavedShape(function() {
        self.showSavedShape(show);
      });
      return;
    }
    if (show) {
      map.addOverlay(this.savedShape);
    } else {
      map.removeOverlay(this.savedShape);
    }
    if (this.toolbar.shapesObjs) {
      this.toolbar.shapesObjs[GTFS_TRIPTOOLBAR_SHAPES_SAVED].checked = show;
    }
  };

  GTFS.Data.Trip.prototype.getSavedShape = function(callback) {
    if (!this.savedShape) {
      var self = this;
      this.fetcher.getSavedShape(this, this.id, (function(data) {
        var points = data;
        var latlngs = [];
        for (var i = 0; i < points.length; ++i) {
          var ll = new GLatLng(points[i][0], points[i][1]);
          latlngs.push(ll);
        }
        var color = GTFS.Data.Trip.colors.saved;
        self.savedShape = new GPolyline(latlngs, color, 4);
        if (callback) callback();
      }));
    }
  };

  GTFS.Data.Trip.prototype.showCurrentShape = function(show) {
    if (show) {
      this.shape.showLegs();
    } else {
      this.shape.hideLegs();
    }
    this.toolbar.shapesObjs[GTFS_TRIPTOOLBAR_SHAPES_CURRENT].checked = show;
  };

  /**
   * Show programmed shapes with async invoke
   * @param {Boolean} show to show or not
   * @param {Boolean} force if GDirections forced
   */
  GTFS.Data.Trip.prototype.showProgrammedShape = function(show, force) {
    if(GTFS.Data.dirShapes[this.shapeId] && !this.dirShape){
      this.dirShape = GTFS.Data.dirShapes[this.shapeId];
    }
    if(this.dirShape && !force){
      if (show) this.dirShape.showLegs(true);
      else this.dirShape.hideLegs(true);
      if(this.toolbar) {
      this.toolbar.shapesObjs[GTFS_TRIPTOOLBAR_SHAPES_PROGRAMMED].checked = show;
      this.toolbar.shapesObjs[GTFS_TRIPTOOLBAR_SHAPES_PROGRAMMED].disabled = false;
      }
      return;
    }
    var self = this;
    if (!this.dirShape || force) {
      if (this.inAsyncVerify) return;
      this.inAsyncVerify = true;
      this.getDirShape(function(isSuc) {
        self.inAsyncVerify = false;
        if (!isSuc) return;
        self.showProgrammedShape(false, false);
        self.merge();
      },
      force);
    }
  };

  GTFS.Data.Stop.prototype.setLatLng = function(latlng) {
    this.latlng = latlng;
    this.lat = latlng.lat();
    this.lng = latlng.lng();
  };

  GTFS.Data.Stop.prototype.getLatLng = function() {
    if (!this.latlng) {
      this.latlng = new GLatLng(this.lat, this.lng);
    }
    return this.latlng;
  };

  GTFS.Data.Stop.prototype.updateLatLng = function(latlng, callback) {
    this.fetcher.updateStopLatLng(this, this.id, latlng, function() {
      this.setLatLng(latlng);
      if (callback) callback();
    });
  };

  GTFS.Data.Stop.prototype.toStation = function(tripId, callback) {
    if (this.parentStation) {
      if (callback) callback(this.station);
      return;
    }
    this.fetcher.toStation(this, tripId, this.id, this.sequence, function(data) {
      var station = this.update2Station(data);
      if (callback) callback(station);
    });
  };

  GTFS.Data.Stop.prototype.update2Station = function(data) {
    var stop = new GTFS.Data.Stop();
    for (var o in this) {
      stop[o] = this[o];
    }
    stop.id = data;
    stop.parentStation = this.id;
    // this = stop;
    GTFS.Data.stops[stop.id] = stop;
    return stop;
  };

  GTFS.Data.Stop.prototype.init = function() {
    this.initIcon();
  };

  // Stops member functions
  GTFS.Data.Stop.prototype.getQuery = function(point) {
    return this.getPoint(this.marker.getLatLng());
  };

  GTFS.Data.Stop.prototype.getPoint = function(point) {
    return point.lat().toFixed(9) + "," + point.lng().toFixed(9);
  };

  GTFS.Data.Stop.prototype.setPreStop = function(stop) {
    this.preStop = stop;
  };

  GTFS.Data.Stop.prototype.setSufStop = function(stop) {
    this.sufStop = stop;
  };

  GTFS.Data.Stop.prototype.getMarker = function(data) {
    return this.marker;
  };

  GTFS.Data.Stop.prototype.createMarker = function(data) {
    var markerOpts = new Object();
    markerOpts.icon = GTFS.Data.icons.normal;
    this.image = GTFS.Data.icons.normal.image;
    if (this.arrival_time) {
      markerOpts.labelText = this.arrival_time;
    }
    markerOpts.draggable = true;
    markerOpts.labelClass = "tooltip";
    markerOpts.labelOffset = new GSize(6, - 20);
    var ll = new GLatLng(this.lat, this.lng);
    var marker = new LabeledMarker(ll, markerOpts);
    marker.disableDragging();
    this.marker = marker;
  };

  GTFS.Data.Stop.prototype.showMarker = function(isSuc) {
    map.addOverlay(this.marker);
  };

  GTFS.Data.Stop.prototype.hideMarker = function(isSuc) {
    map.removeOverlay(this.marker);
  };

  GTFS.Data.Stop.prototype.initDir = function() {
    this.sufEndGDir = true;
    this.preEndGDir = true;

    GEvent.bind(this.marker, 'click', this, this.onMarkerClick);
    GEvent.bind(this.marker, 'dragstart', this, function() {
      this.from = this.marker.getLatLng();
    });
    GEvent.bind(this.marker, 'dragend', this, this.onDragEnd);
  };

  GTFS.Data.Stop.prototype.onMarkerClick = function() {
    if (GTFS.Data.Stop.preDirLeg) {
      GTFS.Data.Stop.preDirLeg.hide();
    }
    if (GTFS.Data.Stop.sufDirLeg) {
      GTFS.Data.Stop.sufDirLeg.hide();
    }
    if (!this.sufEndGDir && ! this.preEndGDir) return;
    if (!this.trip.editMode) {
      var div = this.getStopEditNode();
      this.marker.openInfoWindow(div);
      return;

    }
    if (!this.dired) {
      this.startDir();
      this.dired = true;
    } else {
      this.dired = false;
    }
  };

  GTFS.Data.Stop.prototype.onDragEnd = function() {
    this.to = this.marker.getLatLng();
    if (!this.parentStation) {
      if (confirm("this stop is a main stop, do you want to create a new stop binding to this station?\n" + "Yes, create a new station related to the stop.\n" + "No, only move this stop to a new position")) {
        //this.copyStation(this.from, this.to);
        this.shape.history.execute(new GTFS.Command.Stop.Copy(this.shape, this, this.from, this.to));
        return;
      }
    }
    this.shape.history.execute(new GTFS.Command.Stop.Move(this));
  };

  GTFS.Data.Stop.prototype.copyStation = function(from, to) {
    var self = this;
    this.toStation(this.trip.id, function(station) {
      station.latlng = to;
      self.updateStop(station, function() {
        self.trip.saveShape(null, true);
      });
    });
  }

  GTFS.Data.Stop.prototype.move = function(from, to, callback) {
    var self = this;
    this.latlng = to;
    this.updateStop(null, function() {
      self.trip.saveShape(null, true);
      if (callback) callback();
    });
  }

  GTFS.Data.Stop.prototype.getStopEditNode = function() {
    var div = $C('div');
    // div.innerHTML = "HELL";
    var getA = function(str) {
      var a = $C('a');
      a.href = "javascript:void(0);";
      a.innerHTML = str;
      return a;
    };
    var insertBefore = getA("before");
    var insertAfter = getA("after");
    var copy = getA("copy");

    var remove = getA("remove it");

    var addLine = $C('p');
    addLine.appendChild($T('insert:'));
    addLine.appendChild($C('br'));
    addLine.appendChild($T('    '));
    addLine.appendChild(insertBefore);
    addLine.appendChild($C('br'));
    addLine.appendChild($T('    '));
    addLine.appendChild(insertAfter);
    addLine.appendChild($C('br'));
    addLine.appendChild($C('br'));
    if (!this.parentStation) {
      addLine.appendChild(copy);
      GEvent.bindDom(copy, 'click', this, this.copy);
    }

    var deleteLine = $C('p');
    deleteLine.appendChild(remove);

    div.appendChild(addLine);
    div.appendChild(deleteLine);

    var self= this;

    GEvent.bindDom(insertBefore, 'click', this, this.insertBefore);
    GEvent.bindDom(insertAfter, 'click', this, this.insertAfter);
    GEvent.bindDom(remove, 'click', this, this.remove);
    return div;
  };

  GTFS.Data.Stop.prototype.insertBefore = function() {
    this.shape.history.execute(new GTFS.Command.Stop.InsertBefore(this.shape, this));
    // this.trip.insertBefore(this);
  };

  GTFS.Data.Stop.prototype.copy = function() {
    var self = this;
    if (confirm("You are in the process of creating a new stop to replace current stop, are you sure to proceed?")) {
        this.shape.history.execute(new GTFS.Command.Stop.Copy(this.shape, this, this.getLatLng(), this.getLatLng()));
    }
  };

  GTFS.Data.Stop.prototype.insertAfter = function() {
    this.shape.history.execute(new GTFS.Command.Stop.InsertAfter(this.shape, this));
    // this.trip.insertAfter(this);
  };

  GTFS.Data.Stop.prototype.remove = function() {
    this.shape.history.execute(new GTFS.Command.Stop.Remove(this.shape, this));
    // this.trip.remove(this);
  };

  GTFS.Data.Stop.prototype.updateStop = function(station, callback) {
    for(var o in station){
      this[o] = station[o];
    }
    this.updateLatLng(this.getLatLng(), function() {});
    var image = GTFS.Data.icons.edit.image;
    this.setImage(image);
    this.onChange(callback);
  };

  GTFS.Data.Stop.prototype.onChange = function(callback) {
    var self = this;
    if (this.sufLeg) {
      this.sufLeg.onStopChanged(function() {
        if (self.preLeg) {
          self.preLeg.onStopChanged(callback);
        }
      });
    } else if (this.preLeg) {
      this.preLeg.onStopChanged(callback);
    }
  };

  GTFS.Data.Stop.prototype.getImage = function() {
    return this.image;
  }

  GTFS.Data.Stop.prototype.setImage = function(image) {
    this.image = image;
    this.marker.setImage(image);
  }

  GTFS.Data.Stop.prototype.setPos = function(pos) {
    this.marker.setLatLng(pos);
  }

  GTFS.Data.Stop.prototype.startDir = function(callback) {

    var self = this;
    if (this.preStop) {
      this.startPreDir(function() {
        if (self.sufStop) {
          self.startSufDir(callback);
          return;
        }
        if (callback) callback();
      });

    } else if (self.sufStop) {
      self.startSufDir(callback);
      return;
    }
  };

  GTFS.Data.Stop.prototype.startPreDir = function(callback) {
    var self = this;
    this.preEndGDir = false;
    var from = getPoint(this.preStop.marker.getLatLng());
    var to = getPoint(this.marker.getLatLng());
    var query = "from: " + from + " to: " + to;
    GetDirPoly(query, function(poly) {
      var color = GTFS.Data.Trip.colors.preDir;
      if (!GTFS.Data.Stop.preDirLeg) {
        GTFS.Data.Stop.preDirLeg = new GTFS.Data.Leg();
        GTFS.Data.Stop.preDirLeg.sufStop = self;
      }
      GTFS.Data.Stop.preDirLeg.onModified(poly, color, true);
      self.preEndGDir = true;
      if (callback) callback();
    });
  };

  GTFS.Data.Stop.prototype.startSufDir = function(callback) {
    var self = this;
    this.sufEndGDir = false;
    var to = getPoint(this.sufStop.marker.getLatLng());
    var from = getPoint(this.marker.getLatLng());
    var query = "from: " + from + " to: " + to;
    GetDirPoly(query, function(poly) {
      var color = GTFS.Data.Trip.colors.sufDir;
      if (!GTFS.Data.Stop.sufDirLeg) {
        GTFS.Data.Stop.sufDirLeg = new GTFS.Data.Leg();
        GTFS.Data.Stop.sufDirLeg.preStop = self;
      }
      GTFS.Data.Stop.sufDirLeg.onModified(poly, color, true);
      self.sufEndGDir = true;
      if (callback) callback();
    });
  };

  GTFS.Data.Stop.prototype.formatTime = function(secSinceMidnight) {
    var hours = Math.floor(secSinceMidnight / 3600);
    var suffix = "";

    if (twelveHourTime) {
      suffix = (hours >= 12) ? "p": "a";
      suffix += (hours >= 24) ? " next day": "";
      hours = hours % 12;
      if (hours === 0) hours = 12;
    }
    var minutes = Math.floor(secSinceMidnight / 60) % 60;
    var seconds = secSinceMidnight % 60;
    if (seconds === 0) {
      return hours + ":" + leadingZero(minutes) + suffix;
    } else {
      return hours + ":" + leadingZero(minutes) + ":" + leadingZero(seconds) + suffix;
    }
  };

  // Shape member functions




  GTFS.Data.Shape.prototype.getSequence = function(aStop) {
    for (var i = 0, stop; stop = this.stops[i]; i++) {
      if (stop === aStop) {
        return i;
      }
    }
    return - 1;
  };


  GTFS.Data.Shape.prototype.getPreviousStopTime = function(stopIn) {
    for (var i = 0, stop; stop = this.stops[i]; i++) {
      if (stop === stopIn) {
        return stop.sequence;
      }
    }
    return - 1;
  };

  GTFS.Data.Shape.prototype.getInsertLatLng = function(stop, idx, isBefore) {
    if (idx === 0) {
      return {
        lat: stop.lat - 0.001,
        lng: stop.lng - 0.001
      };
    }
    var start, end;
    if (isBefore) {
      start = this.stops[idx - 1];
      end = stop;
    } else {
      start = stop;
      end = this.stops[idx + 1];
    }
    return {
      lat: (start.lat / 1 + end.lat / 1) / 2,
      lng: (start.lng / 1 + end.lng / 1) / 2
    };
  }

  GTFS.Data.Shape.prototype.insertBefore = function(stop, callback) {
    var idx = this.getSequence(stop);
    if (idx < 0) {
      if (callback) callback(null);
    }

    var lat, lng;
    var seq = stop.sequence;
    var latlng = this.getInsertLatLng(stop, idx, true);
    lat = latlng.lat;
    lng = latlng.lng;
    this.insertOnce(stop, seq, lat, lng, true, callback);
  };

  GTFS.Data.Shape.prototype.insertOldStop = function(stop, seq, idx, callback) {
    var self = this;
    var tid = this.id;
    var sid = stop.id;
    this.fetcher.insertOldStop(self, this.id, this.rid, stop.id, seq, function() {
      self.onInsertOldStop(stop, idx);
      if (callback) callback();
    });

  }

  GTFS.Data.Shape.prototype.onInsertOldStop = function(stop, idx) {

    var preStop = null;
    var sufStop = null;
    if (stop.preLeg) {
      stop.preLeg.hide();
    }
    if (stop.sufLeg) {
      stop.sufLeg.hide();
    }
    if (idx > 0) {
      preStop = this.stops[idx - 1];
      preStop.setSufStop(stop);
      if (preStop.sufLeg) {
        preStop.sufLeg.hide();
      }
      preStop.sufLeg = new GTFS.Data.Leg();
      stop.preLeg = preStop.sufLeg;
      stop.preLeg.setStops(preStop, stop);
    }
    if (idx < this.stops.length) {
      sufStop = this.stops[idx];
      sufStop.setPreStop(stop);
      stop.setSufStop(sufStop);
      if (sufStop.preLeg) {
        sufStop.preLeg.hide();
      }
      sufStop.preLeg = new GTFS.Data.Leg();
      stop.sufLeg = sufStop.preLeg;
      stop.sufLeg.setStops(stop, sufStop);
    }
    this.stops.splice(idx, 0, stop);
    for (var i = idx + 1, stop; stop = this.stops[i]; i++) {
      stop.sequence += 1;
    }
  }

  GTFS.Data.Shape.prototype.insertAfter = function(stop, callback) {
    var idx = this.getSequence(stop);
    if (idx < 0) {
      if (callback) callback(null);
    }
    var lat, lng;
    var seq = stop.sequence + 1;
    var latlng = this.getInsertLatLng(stop, idx, false);
    lat = latlng.lat;
    lng = latlng.lng;
    this.insertOnce(stop, seq, lat, lng, false, callback);
  };

  GTFS.Data.Shape.prototype.insertOnce = function(stop, seq, lat, lng, isBefore, callback) {
    this.fetcher.insertStopByShape(this, this.id, this.rid, stop.id, seq, lat, lng, function(id) {
      this.newStop(stop, id, lat, lng, isBefore, callback);
    });
  }

  GTFS.Data.Shape.prototype.newStop = function(stop, id, lat, lng, isBefore, callback) {
    // var newstoptime = new GTFS.Data.StopTimes();
    var newstop = new GTFS.Data.Stop();
    for (var o in stop) {
      newstop[o] = stop[o];
    }
    newstop.parentStation = null;
    newstop.id = id;
    newstop.lat = lat;
    newstop.lng = lng;
    newstop.createMarker();
    newstop.initDir();
    newstop.showMarker();

    this.insertStopTimeCallback(stop, newstop, isBefore, callback);
  }

  GTFS.Data.Shape.prototype.localCopyStop = function(stop, id, parent) {
    // var newstoptime = new GTFS.Data.StopTimes();
    var newstop = new GTFS.Data.Stop();
    for (var o in stop) {
      newstop[o] = stop[o];
    }
    newstop.parentStation = parent;
    newstop.id = id;
    newstop.lat = stop.getLatLng().lat();
    newstop.lng = stop.getLatLng().lng();
    newstop.createMarker();
    newstop.initDir();
    newstop.showMarker();
    return newstop;
  }

  GTFS.Data.Shape.prototype.localInsertStop = function(stop, idx, callback) {
    var preStop = null, sufStop = null;
    var tag = false;
    if(idx == 0){
      sufStop = this.stops[idx];
      sufStop.setPreStop(stop);
      stop.setSufStop(sufStop);
      stop.sufLeg = new GTFS.Data.Leg();
      sufStop.preLeg = stop.sufLeg;
      stop.sufLeg.setStops(stop, sufStop);
      stop.sufLeg.onStopChanged();
    } else if (idx == this.stops.length) {
      preStop = this.stops[idx - 1];
      preStop.setSufStop(stop);
      stop.setPreStop(preStop);
      stop.preLeg = new GTFS.Data.Leg();
      stop.preLeg.setStops(preStop, stop);
      stop.preLeg.onStopChanged();
    } else if (idx > 0 && idx < this.stops.length){
      preStop = this.stops[idx - 1];
      sufStop = this.stops[idx];
      preStop.setSufStop(stop);
      stop.setPreStop(preStop);
      stop.preLeg = preStop.sufLeg;
      stop.preLeg.setStops(preStop, stop);
      stop.preLeg.onStopChanged();
      sufStop = this.stops[idx];
      sufStop.setPreStop(stop);
      stop.setSufStop(sufStop);
      stop.sufLeg = new GTFS.Data.Leg();
      sufStop.preLeg = stop.sufLeg;
      stop.sufLeg.setStops(stop, sufStop);
      tag = true;
    } else {
      return;
    }
    stop.sequence = idx + 1;
    this.stops.splice(idx, 0, stop);
    for(var i = idx + 1, tmp; tmp = this.stops[i]; i++){
      tmp.sequence = tmp.sequence * 1 + 1
    }

    for(var j = 0, trip; trip = this.route.shapes[this.id][j]; j++){
      var stoptime = new GTFS.Data.StopTime();
      stoptime.arrival_time = "";
      stoptime.departure_time = "";
      stoptime.trip = trip;
      stoptime.sequence = stop.sequence * 1;
      stoptime.stop = stop;
      if(trip.stoptimes) {
        trip.stoptimes.splice(idx, 0, stoptime);
      }
    }
    if(tag){
      stop.sufLeg.onStopChanged(function(){
        if(callback) callback(stop);
      });
      return;
    }
    if(callback){
      callback();
    }
  }

  GTFS.Data.Shape.prototype.localRemoveStop = function(stop, callback) {
    var idx = this.getSequence(stop);
    var callable = false;
    if(idx < 0){
      return;
    }
    
    for (var i = idx + 1, tmp; tmp = this.stops[i]; i++) {
      tmp.sequence -= 1;
    }
    if (stop.preLeg && stop.sufLeg) {
      var preStop = this.stops[idx - 1];
      var sufStop = this.stops[idx + 1];
      preStop.sufLeg.hide();
      preStop.sufLeg = stop.sufLeg;
      preStop.setSufStop(sufStop);
      sufStop.preLeg = stop.sufLeg;
      sufStop.setPreStop(preStop);
      stop.sufLeg.setStops(preStop, sufStop);
      callable = true;
    } else if (stop.sufLeg) {

      stop.sufLeg.hide();
      var sufStop = this.stops[idx + 1];
      sufStop.preLeg = null
      sufStop.setPreStop(null);

    } else if (stop.preLeg) {
      stop.preLeg.hide();
      var preStop = this.stops[idx - 1];
      preStop.sufLeg = null;
      preStop.setSufStop(null);
    }

    this.stops.splice(idx, 1);

    for(var j = 0, trip; trip = this.route.shapes[this.id][j]; j++){
      if(trip.stoptimes && trip.stoptimes.length){
        var len = trip.stoptimes.length;
        if(idx >= len) continue;
        trip.stoptimes.splice(idx, 1);
      }
    }
    if(callable) {
      stop.sufLeg.onStopChanged(callback);
    }  else {
      if(callback) callback();
    }
  }

  GTFS.Data.Shape.prototype.localReplaceStop = function(replace, stop) {
    var preStop = replace.preStop;
    var sufStop = replace.sufStop;
    var preLeg = replace.preLeg;
    var sufLeg = replace.sufLeg;
    if(preStop) {
      preStop.setSufStop(stop);
      stop.setPreStop(preStop);
    }
    if(sufStop) {
      sufStop.setPreStop(stop);
      stop.setSufStop(sufStop);
    }
    if(preLeg){
      stop.preLeg = preLeg;
      preLeg.setStops(preStop, stop);
    }
    if(sufLeg){
      stop.sufLeg = sufLeg;
      sufLeg.setStops(stop, sufStop);
    }
    var idx = this.getSequence(replace);
    this.stops.slice(idx, 1, stop);
  }

  GTFS.Data.Shape.prototype.insertStopTimeCallback = function(stoptime, newstoptime, isBefore, callback) {
    this.insertStopTime(stoptime, newstoptime, isBefore, callback);
  };

  GTFS.Data.Shape.prototype.insertStopTime = function(oldStop, newStop, isBefore, callback) {
    var idx = this.getSequence(oldStop);
    if(idx < 0) {
      return;
    }
    if(!isBefore){
      idx += 1;
    }
    this.localInsertStop(newStop, idx, callback);
  };

  GTFS.Data.Shape.prototype.copy = function(stop, callback) {
    var idx = this.getSequence(stop);
    if (idx < 0){
      if (callback) callback();
    }
    var self = this;
    this.fetcher.toStationByShape(this, this.id, this.rid, stop.id, stop.sequence, function(id){
      var newstop = self.localCopyStop(stop, id, stop.id);
      this.localReplaceStop(stop, newstop);
      GTFS.Data.stops[id] = newstop;
      newstop.updateLatLng(stop.getLatLng(), function(){
      stop.hideMarker();
      var image = GTFS.Data.icons.edit.image;
      newstop.setImage(image);
      newstop.onChange(function(){
        callback(newstop);
      });
      });
    });
  }

  GTFS.Data.Shape.prototype.replace = function(stop, newstop, callback) {
    var self = this;
    this.fetcher.replaceStopByShape(this, this.id, this.rid, stop.id, newstop.id, stop.sequence, function(tag){
      if(tag){
        var idx = this.getSequence(stop);
        self.localReplaceStop(stop, newstop);
      }
      if(callback) callback();
    });
  }

  GTFS.Data.Shape.prototype.remove = function(stop, callback) {
    var self = this;
    this.fetcher.removeStopByShape(this, this.id, this.rid, stop.id, stop.sequence, function(id) {
      stop.hideMarker();
      self.localRemoveStop(stop, callback);
    });
  };


  GTFS.Data.Shape.prototype.disableStops = function() {
    for (var i = 0; i < this.stops.length; i++) {
      this.stops[i].marker.disableDragging();
      if (this.stops[i].sufLeg) this.stops[i].sufLeg.enableEditing(false);
    }
  };

  GTFS.Data.Shape.prototype.enableStops = function() {
    for (var i = 0; i < this.stops.length; i++) {
      this.stops[i].marker.enableDragging();
      if (this.stops[i].sufLeg) this.stops[i].sufLeg.enableEditing(true);
    }
  };

  GTFS.Data.Shape.prototype.init = function(stops, notShow) {
    //this.stops = stops;
    this.setStops(stops);
    this.initLegs(notShow);
  };

  GTFS.Data.Shape.prototype.setStops = function(stops) {
    this.stops = stops;
    for(var i = 0; i < this.stops.length; i++){
      this.stops[i].shape = this;
    }
  };

  GTFS.Data.Shape.prototype.initLegs = function(notShow) {
    this.bindShape();
    if(!notShow) this.showLegs();
  };

  /**
   * Get nearest point in points to latlng
   * @param {Array} points array of GLatLng
   * @param {Number} start index to start with
   * @param {GLatLng} latlng
   * @return {Number} index value found, -1 if not
   */
  GTFS.Data.Shape.prototype.getNearestPoint = function(points, start, latlng) {
    if (!points.length) return - 1;
    var pre = 9999999999;
    var found = points.length - 1;
    var cur = - 1;
    for (var i = start; i <= found; i++) {
      var lat = Math.abs(points[i].lat - latlng.lat());
      var lng = Math.abs(points[i].lng - latlng.lng());
      var dis = lat * lat + lng * lng;

      if ((dis < pre)) {
        pre = dis;
        cur = i;
      }
    }
    if (cur > 0) return cur;
    return found;
  };

  GTFS.Data.Shape.prototype.getShapeFromStops = function() {
    var i = 0;
    var self = this;
    var stop = null;
    function nextStop(i) {
      if (i < self.stops.length) {
        stop = self.stops[i];
        if (stop.sufLeg) {
          stop.sufLeg.onStopChanged(function() {
            stop.sufLeg.show();
            nextStop(i + 1);
          });
        } else {
          nextStop(i + 1);
        }
      }
    }
    nextStop(0);
  }

  /**
   * Bind points to the shapes
   */
  GTFS.Data.Shape.prototype.bindShape = function() {
    var start = 0;
    var end = - 1;
    if (!this.data.length) {
      var preStop = this.stops[0];
      var sufStop = null;
      for (var i = 1; i < this.stops.length; i++) {
        sufStop = this.stops[i];
        var leg = new GTFS.Data.Leg();
        leg.setStops(preStop, sufStop);
        sufStop.preLeg = leg;
        preStop.sufLeg = leg;
        preStop = sufStop;
      }
      //this.getShapeFromStops();
      return;
    }
    for (var i = 1; i < this.stops.length; i++) {
      end = this.getNearestPoint(this.data, start, this.stops[i].marker.getLatLng());

      var leg = new GTFS.Data.Leg();
      var points = [];
      for (var j = start; j <= end; j++) {
        points.push(this.data[j]);
        // dist += this.data[j].distanceFrom(this.data[j + 1]);
      }

      var color = GTFS.Data.Trip.colors.def;
      var poly = leg.points2Poly(points, color);
      leg.setPolyline(poly);
      // leg.setData(points, color);
      this.stops[i].preLeg = leg;
      this.stops[i - 1].sufLeg = leg;
      leg.setStops(this.stops[i - 1], this.stops[i]);
      start = end;
    }
  };

  GTFS.Data.Shape.prototype.feedData = function(data) {
    this.hideLegs();
    this.data = data;
    this.bindShape();
  };

  GTFS.Data.Shape.prototype.toData = function() {
    var data = [];
    for (var i = 0; i < this.stops.length - 1; i++) {
      var tmp = this.stops[i].sufLeg.toData();
      for (var j = 0; j < tmp.length; j++) {
        data.push(tmp[j]);
      }
    }
    return data;
  };

  GTFS.Data.Shape.prototype.updateData = function() {
    this.data = this.toData();
  };

  GTFS.Data.Shape.prototype.showLegs = function(isVerify) {
    for (var i = 0, stop; stop = this.stops[i]; i++) {
      var leg = null;
      if (isVerify) {
        leg = stop.sufVerifyLeg;
      } else {
        leg = stop.sufLeg;
      }
      if (leg) leg.show();
    }
  };

  GTFS.Data.Shape.prototype.enableLegs = function() {
    for (var i = 0, stop; stop = this.stops[i]; i++) {
      if (stop.sufLeg) stop.sufLeg.enableEditing(true);
    }
  };

  GTFS.Data.Shape.prototype.hideLegs = function(isVerify) {
    for (var i = 0, stop; stop = this.stops[i]; i++) {
      var leg = null;
      if (isVerify) {
        leg = stop.sufVerifyLeg;
      } else {
        leg = stop.sufLeg;
      }
      if (leg) leg.hide();
    }
  };

  // Leg member functions
  // DragMarker related functions start
  GTFS.Data.Leg.prototype.getDragMarker = function(point) {
    var self = this;
    if (!GTFS.Data.Leg.dragMarker) {
      var icon = new GIcon();
      icon.iconSize = new GSize(8, 8);
      icon.iconAnchor = new GPoint(4, 4);
      icon.infoWindowAnchor = new GPoint(4, 0);
      var redIcon8 = new GIcon(icon, "/file/mm_20_red.png", null, "");
      GTFS.Data.Leg.dragMarker = new GMarker(point, {
        icon: redIcon8,
        draggable: true,
        bouncy: false
      });
    }
    if (GTFS.Data.Leg.dragMarkerStartHandler) {
      GEvent.removeListener(GTFS.Data.Leg.dragMarkerStartHandler);
    }
    if (GTFS.Data.Leg.dragMarkerEndHandler) {
      GEvent.removeListener(GTFS.Data.Leg.dragMarkerEndHandler);
    }
    GTFS.Data.Leg.dragMarkerStartHandler = GEvent.addListener(GTFS.Data.Leg.dragMarker, 'dragstart', function() {
      GTFS.Data.Leg.dragging = true;
    });
    GTFS.Data.Leg.dragMarkerEndHandler = GEvent.addListener(GTFS.Data.Leg.dragMarker, 'dragend', function(point) {
      GTFS.Data.Leg.dragging = false;
      if (self.preStop) {
        self.reGDir(point);
      }
      self.hideDragMarker();
    });

    GTFS.Data.Leg.dragMarker.setLatLng(point);
    return;
  };

  GTFS.Data.Leg.prototype.showDragMarker = function(point) {
    var shortDist = 999999999;
    var idx = 0;
    var count = this.polyline.getVertexCount();
    var poly = this.polyline;
    for (var i = 1; i < count - 1; i++) {
      var dist = point.distanceFrom(poly.getVertex(i));
      if (dist < shortDist) {
        idx = i;
        shortDist = dist;
      }
    }
    if (idx) {
      this.getDragMarker(poly.getVertex(idx));
      map.addOverlay(GTFS.Data.Leg.dragMarker);
    }
  };

  GTFS.Data.Leg.prototype.hideDragMarker = function() {
    if (GTFS.Data.Leg.dragMarker) {
      map.removeOverlay(GTFS.Data.Leg.dragMarker);
    }
  };

  // DragMarker related functions end
  GTFS.Data.Leg.prototype.enableEditing = function(enable) {
    this.enable = enable;
  };

  GTFS.Data.Leg.prototype.setStops = function(pre, suf) {
    this.preStop = pre;
    this.sufStop = suf;
  };

  GTFS.Data.Leg.prototype.updateStopLeg = function() {
    var leg = null;
    var stop = null;
    if (this.preStop && this.preStop.sufLeg) {
      stop = this.preStop;
      leg = this.preStop.sufLeg;
    } else if (this.sufStop && this.sufStop.preLeg) {
      stop = this.sufStop;
      leg = this.sufStop.preLeg;
    }
    if (!leg) {
      return;
    }

    stop.shape.history.execute(new GTFS.Command.Leg.Use(stop, leg, this));

  };

  GTFS.Data.Leg.prototype.setStopLeg = function(stop, leg) {

  }

  GTFS.Data.Leg.prototype.onStopChanged = function(callback) {
    if (this.preStop && this.sufStop) {
      var self = this;
      var start = this.preStop.getLatLng();
      var end = this.sufStop.getLatLng();

      var query = "from: " + getPoint(start) + " to: " + getPoint(end);

      GetDirPoly(query, function(poly) {
        self.onModified(poly);
        if (callback) callback();
      })
    }
  };

  GTFS.Data.Leg.prototype.onModified = function(poly, color) {
    color = color || GTFS.Data.Trip.colors.modified;
    this.hide();
    this.setPolyline(poly, color, true);
    this.show();
    this.enable = true;
  };

  GTFS.Data.Leg.prototype.getPoint = function(point) {
    return point.lat().toFixed(9) + "," + point.lng().toFixed(9);
  };

  GTFS.Data.Leg.prototype.reGDir = function(point) {
    var self = this;
    var start = this.polyline.getVertex(0);
    var end = this.polyline.getVertex(this.polyline.getVertexCount() - 1)
    var query = "from: " + getPoint(start) + " to: " + getPoint(point) + " to: " + getPoint(end);
    GetDirPoly(query, function(poly) {
      self.preStop.shape.history.execute(new GTFS.Command.Leg.Drag(self, poly));
      // self.onModified(poly);
    });
  };

  GTFS.Data.Leg.prototype.toData = function() {
    var data = [];
    if(!this.polyline) return data
    var count = this.polyline.getVertexCount();
    for (var i = 0; i < count; i++) {
      var point = this.polyline.getVertex(i);
      data.push({
        lat: point.lat(),
        lng: point.lng()
      });
    }
    return data;
  };

  GTFS.Data.Leg.prototype.clone = function() {
    var leg = new GTFS.Data.Leg();
    var color = this.color;
    var points = this.toData();
    leg.polyline = null;
    var poly = this.points2Poly(points, color);
    leg.setPolyline(poly);
    leg.enable = this.enable;
    leg.preStop = this.preStop;
    leg.sufStop = this.sufStop;
    return leg;
  }

  GTFS.Data.Leg.prototype.replace = function(leg) {
    this.color = leg.color;
    if (this.polyline) {
      map.removeOverlay(this.polyline);
    }
    this.polyline = leg.polyline;
    this.enable = leg.enable;
    this.preStop = this.preStop;
    this.sufStop = this.sufStop;
  }

  GTFS.Data.Leg.prototype.getPolyline = function() {
    return this.polyline;
  }

  GTFS.Data.Leg.prototype.newPolyline = function() {
    return this.points2Poly(this.toData(), this.color);
  }


  GTFS.Data.Leg.prototype.setPolyline = function(polyline, color, enable) {
    this.enable = enable;
    if (this.polyline) {
      map.removeOverlay(this.polyline);
    }
    this.polyline = polyline;
    if (color) this.setColor(color);
    if(this.polyline){
      GEvent.bind(this.polyline, 'click', this, this.onClick);
      GEvent.bind(this.polyline, 'mousemove', this, this.onMouseOver);
      GEvent.bind(this.polyline, 'mouseout', this, this.onMouseOut);
    }
  }

  GTFS.Data.Leg.prototype.points2Poly = function(points, color) {
    var latlngs = [];
    for (var i = 0; i < points.length; i++) {
      latlngs.push(new GLatLng(points[i].lat, points[i].lng));
    }
    color = color || GTFS.Data.Trip.colors.def;
    return new GPolyline(latlngs, color, 4);
  }

  // Leg UI Handlers
  GTFS.Data.Leg.prototype.onClick = function() {

    // make sure only the GDirectioned polylilne can be updated to the
    // current shapes
    if (GTFS.Data.Stop.preDirLeg !== this && GTFS.Data.Stop.sufDirLeg !== this) return;
    var dom = $C("div");
    var a = $C("a");
    a.href = "javascript:void(0);";
    a.innerHTML = "use this programmed leg as the current leg";
    dom.appendChild(a);
    GEvent.bindDom(a, 'click', this, function() {
      map.closeInfoWindow();
      this.updateStopLeg();
    });
    map.openInfoWindow(GTFS.mousePoint, dom);
  }

  GTFS.Data.Leg.prototype.onMouseOver = function() {
    if (!this.enable || GTFS.Data.Leg.dragging) return;
    this.showDragMarker(GTFS.mousePoint);
    // self.polyline.enableEditing();
  }

  GTFS.Data.Leg.prototype.onMouseOut = function() {
    if (!this.enable || GTFS.Data.Leg.dragging) return;
    this.hideDragMarker();
    // self.polyline.disableEditing();
  }

  // Leg UI Handlers
  GTFS.Data.Leg.prototype.show = function() {
    if (!this.polyline) {
      var latlngs = [this.preStop.getLatLng(), this.sufStop.getLatLng()];
      var color = GTFS.Data.Trip.colors.current;
      this.polyline = new GPolyline(latlngs, color, 4);
    }
    map.addOverlay(this.polyline);
  };

  GTFS.Data.Leg.prototype.hide = function() {
    if (this.polyline) map.removeOverlay(this.polyline);
  };

  GTFS.Data.Leg.prototype.setColor = function(color) {
    this.color = color;
    if (this.polyline) {
      this.polyline.setStrokeStyle({
        color: color
      });
    }
  };

  var GTFS_TRIPTOOLBAR_SHAPES = 0;
  var GTFS_TRIPTOOLBAR_EDITOR = 1;

  var GTFS_TRIPTOOLBAR_SHAPES_ORIGINAL = 0;
  var GTFS_TRIPTOOLBAR_SHAPES_PROGRAMMED = 1;
  var GTFS_TRIPTOOLBAR_SHAPES_SAVED = 2;
  var GTFS_TRIPTOOLBAR_SHAPES_CURRENT = 3;
  var GTFS_TRIPTOOLBAR_SHAPES_END = 4;

  var GTFS_TRIPTOOLBAR_EDITOR_VERIFY = 0;
  var GTFS_TRIPTOOLBAR_EDITOR_SAVE = 1;
  var GTFS_TRIPTOOLBAR_EDITOR_REVERT = 2;
  var GTFS_TRIPTOOLBAR_EDITOR_UNDO = 3;
  var GTFS_TRIPTOOLBAR_EDITOR_REDO = 4;
  var GTFS_TRIPTOOLBAR_EDITOR_MERGE = 5;
  var GTFS_TRIPTOOLBAR_EDITOR_END = 6;

  /**
   * Toolbar for trip
   * @constructor
   */
  GTFS.Toolbar.Trip = function() {
    this.shapesObjs = [];
    this.editorObjs = [];

    for (var i = 0; i < GTFS_TRIPTOOLBAR_SHAPES_END; i++) {
      this.shapesObjs.push(null);
    }
    for (i = 0; i < GTFS_TRIPTOOLBAR_EDITOR_END; i++) {
      this.editorObjs.push(null);
    }
  };

  GTFS.Toolbar.Trip.prototype = new GControl();

  /**
   * Register callback for different option value
   * @param {Number} value idx of options
   * @param {Object} self this pointer for callback
   * @param {Function} callback callback on option event
   */
  GTFS.Toolbar.Trip.prototype.registerOption = function(value, self, callback) {
    switch (value) {
    case GTFS_TRIPTOOLBAR_SHAPES:
      GEvent.bindDom(this.editSwitcher, 'click', this, function() {
    	if(this.editorSwitcher.checked){
          this.selectShapes();
    	}
        callback.call(self);
      });
      break;
    case GTFS_TRIPTOOLBAR_EDITOR:
      GEvent.bindDom(this.editSwitcher, 'click', this, function() {
        //this.selectEditor();
        callback.call(self, this.editSwitcher.checked);
      });
      break;
    default:
      break;
    }
  };

  /**
   * Select the shapes option
   */
  GTFS.Toolbar.Trip.prototype.selectShapes = function() {
    this.shapesTab.className = "TBTabSelected";
    this.editorTab.className = "ToolBarTab";
    this.shapesContent.style.display = "";
    this.editorContent.style.display = "none";
  };

  /**
   * Select the editor option
   */
  GTFS.Toolbar.Trip.prototype.selectEditor = function() {
    this.editorTab.className = "TBTabSelected";
    this.shapesTab.className = "ToolBarTab";
    this.shapesContent.style.display = "none";
    this.editorContent.style.display = "";
  };

  /**
   * Register callback for different shapes
   * @param {Number} value index of shapesObj
   * @param {Object} self this pointer to callback
   * @param {FUnction} callback function to be invoked when event occurs
   */
  GTFS.Toolbar.Trip.prototype.registerShapes = function(value, self, callback) {
    GEvent.bindDom(this.shapesObjs[value], 'click', this, function() {
      callback.call(self, this.shapesObjs[value].checked);
    });
  };

  /**
   * Register callback for different editor options
   * @param {Number} value index of editorObj
   * @param {Object} self this pointer to callback
   * @param {Function} callback function to be invoked when event occurs
   */
  GTFS.Toolbar.Trip.prototype.registerEditor = function(value, self, callback) {
    GEvent.bindDom(this.editorObjs[value], 'click', this, function() {
      callback.call(self);
    });
  };
  
  GTFS.Toolbar.Trip.prototype.getCheckBox = function(pDom, tag, text){
      var dom = document.createElement('input');
      dom.type = "checkbox";
      dom.checked = tag;
      pDom.appendChild(dom);
      pDom.appendChild($T(text));
      return dom;	  
  }

  /**
   * Init shapes dom info
   * @param {HTMLElement} dom container of the options
   */
  
  GTFS.Toolbar.Trip.prototype.initShapes = function(dom) {

    var shapes = document.createElement('span');
    shapes.className = "editor";
    dom.appendChild(shapes);
/*
    var getCheckBox = function(tag, text) {
      var dom = document.createElement('input');
      dom.type = "checkbox";
      dom.checked = tag;
      shapes.appendChild(dom);
      shapes.appendChild(document.createTextNode(text));
      return dom;
    };
    */
    this.shapesObjs[GTFS_TRIPTOOLBAR_SHAPES_ORIGINAL] = this.getCheckBox(shapes, true, 'Original');
    this.shapesObjs[GTFS_TRIPTOOLBAR_SHAPES_PROGRAMMED] = this.getCheckBox(shapes, false, 'Programmed');
    this.shapesObjs[GTFS_TRIPTOOLBAR_SHAPES_SAVED] = this.getCheckBox(shapes, false, 'Saved');
    this.shapesObjs[GTFS_TRIPTOOLBAR_SHAPES_CURRENT] = this.getCheckBox(shapes, false, 'Current');
  };
  
  /**
   * Init editor dom info
   * @param {HTMLElement} dom container of the options
   */
  GTFS.Toolbar.Trip.prototype.initEditor = function(dom) {
	
    var editor = document.createElement('span');
    editor.className = "editor";
    dom.appendChild(editor);
    
    this.editSwitcher = this.getCheckBox(editor, false, 'Edit ');

    var getButton = function(text) {
      var archor = document.createElement('a');
      archor.href = "javascript:void(0)";
      archor.innerHTML = text;
      return archor;
    };
    var verify = getButton('Verify');
    var save = getButton('Save');
    var revert = getButton('Revert');
    var undo = getButton('Undo');
    var redo = getButton("Redo");
    var merge = getButton('Merge');
    save.style.display = "none";
    merge.style.display = "none";

    //
    revert.style.color = "#ccc";
    undo.style.color = "#ccc";
    redo.style.color = "#ccc";
    revert.style.cursor = "text";
    undo.style.cursor = "text";
    redo.style.cursor = "text";

    //
    verify.innerHTML = "Generate Shape";
    verify.title = "Generate shape for this trip";

    //

    this.editorObjs[GTFS_TRIPTOOLBAR_EDITOR_VERIFY] = verify;
    this.editorObjs[GTFS_TRIPTOOLBAR_EDITOR_SAVE] = save;
    this.editorObjs[GTFS_TRIPTOOLBAR_EDITOR_REVERT] = revert;
    this.editorObjs[GTFS_TRIPTOOLBAR_EDITOR_UNDO] = undo;
    this.editorObjs[GTFS_TRIPTOOLBAR_EDITOR_REDO] = redo;
    this.editorObjs[GTFS_TRIPTOOLBAR_EDITOR_MERGE] = merge;

    for (var i = 0; i < this.editorObjs.length; i++) {
      if (this.editorObjs[i]) {
        if(this.editorObjs[i].style.display != "none") {
        
        editor.appendChild($T("["));
        editor.appendChild(this.editorObjs[i]);
        editor.appendChild($T(']'));
        }
      }
    }
  };

  /**
   * Initialize Trip tool bar
   * @param {GMap2} map tool bar container map
   */
  GTFS.Toolbar.Trip.prototype.initialize = function(map) {
    var container = document.createElement("div");
    var commands = $C("div");
    commands.className = "TBTabs";
    container.appendChild(commands);

    var tabs = document.createElement('div');
    tabs.className = "TBTabs";

    var shapesTab = document.createElement('span');
    shapesTab.className = "TBTabSelected";
    shapesTab.innerHTML = 'Shapes';
    tabs.appendChild(shapesTab);
    this.shapesTab = shapesTab;

    var editorTab = document.createElement('span');
    editorTab.className = "ToolBarTab";
    editorTab.innerHTML = "Edit&nbsp;";
    tabs.appendChild(editorTab);
    this.editorTab = editorTab;

    var contents = document.createElement('div');
    contents.className = "TBTabs";

    //container.appendChild(tabs);
    container.appendChild(contents);

    var shapesContent = document.createElement('div');
    this.initShapes(shapesContent);
    this.shapesContent = shapesContent;
    contents.appendChild(shapesContent);

    var editorContent = document.createElement('div');
    this.editorContent = editorContent;
    contents.appendChild(editorContent);

    this.initEditor(editorContent);
    shapesContent.style.display = "none";
    editorContent.style.display = "";
    var note = document.createElement('span');
    note.id = "note";
    this.note = note;

    container.appendChild(note);
    map.getContainer().appendChild(container);
    return container;
  };

  // By default, the control will appear in the top left corner of the
  // map with 7 pixels of padding.
  GTFS.Toolbar.Trip.prototype.getDefaultPosition = function() {
    return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(68, 7));
  };

  // Commands for redo / undo functions
  GTFS.Command = function() {};

  GTFS.Command.prototype.execute = function() {};

  GTFS.Command.prototype.unexecute = function() {};

  GTFS.Command.Leg = {};
  GTFS.Command.Stop = {};
  GTFS.Command.Shape = {};
  GTFS.Command.StopTime = {};

  GTFS.Command.Stop.Move = function(stop) {
    this.stop = stop;
    if (stop.preLeg) {
      this.preLeg = stop.preLeg.clone();
    }
    if (stop.sufLeg) {
      this.sufLeg = stop.sufLeg.clone();
    }
    this.from = new GLatLng(stop.from.lat(), stop.from.lng());
    this.to = new GLatLng(stop.to.lat(), stop.to.lng());
    this.image = stop.getImage();

  };

  GTFS.Command.Stop.Move.prototype.execute = function() {
    this.stop.move(this.from, this.to);
  };

  GTFS.Command.Stop.Move.prototype.redo = function() {
    this.stop.move(this.from, this.to);
    this.stop.setPos(this.to);
  };

  GTFS.Command.Stop.Move.prototype.undo = function() {
    var self = this;
    this.stop.move(this.to, this.from, function() {
      self.stop.setImage(self.image);
      self.stop.setPos(self.from);
      self.stop.preLeg.replace(self.preLeg);
      self.stop.sufLeg.replace(self.sufLeg);
      self.stop.preLeg.show();
      self.stop.sufLeg.show();
    });
  };

  GTFS.Command.Leg.Use = function(stop, from, to) {
    this.stop = stop;
    this.fromColor = from.color;
    this.toColor = GTFS.Data.Trip.colors.modified;
    this.fromPolyline = from.clone().getPolyline();
    this.toPolyline = to.clone().getPolyline();
    this.from = from;
    this.to = to;
  };

  GTFS.Command.Leg.Use.prototype.execute = function() {
    this.from.setPolyline(this.toPolyline);
    this.from.setColor(this.toColor);
    this.from.show();
    this.to.show();
    this.stop.trip.saveShape(null, true);
  };
  GTFS.Command.Leg.Use.prototype.redo = function() {
    this.execute();
  };
  GTFS.Command.Leg.Use.prototype.undo = function() {
    this.from.setPolyline(this.fromPolyline);
    this.from.setColor(this.fromColor);
    this.from.show();
    this.to.show();
    this.stop.trip.saveShape(null, true);
  }

  GTFS.Command.Leg.Drag = function(leg, poly) {
    this.leg = leg;
    this.color = leg.color;
    this.prePoly = leg.clone().getPolyline();
    this.poly = poly;
    this.newColor = GTFS.Data.Trip.colors.modified;
  };

  GTFS.Command.Leg.Drag.prototype.execute = function() {
    this.leg.setPolyline(this.poly);
    this.leg.show();
    this.leg.setColor(this.newColor);
  };

  GTFS.Command.Leg.Drag.prototype.redo = function() {
    this.execute();
  };

  GTFS.Command.Leg.Drag.prototype.undo = function() {
    this.leg.setPolyline(this.prePoly);
    this.leg.show();
    this.leg.setColor(this.color);
  };

  GTFS.Command.Shape = {};

  GTFS.Command.Shape.Merge = function(programmed, current) {

  }

  GTFS.Command.Shape.Revert = function(trip) {
    var count = trip.originalShape.getVertexCount();
    var data = [];
    for (var i = 0; i < count; i++) {
      var latlng = trip.originalShape.getVertex(i);
      data.push({
        lat: latlng.lat(),
        lng: latlng.lng(),
        dist: 0.0
      });
    }
    this.oldLegs = [];

    for (var i = 0; i < trip.stops.length; i++) {
      trip.stops[i].sufLeg.hide();
      this.oldLegs.push(trip.stops[i].sufLeg);
    }
  };

  GTFS.Command.Shape.Revert.prototype.execute = function() {
    trip.shapes.feedData(data);
    for (var i = 0; i < trip.stops.length; i++) {
      this.newLegs.push(trip.stops[i].sufLeg);
    }
  };

  GTFS.Command.Shape.Revert.prototype.redo = function() {
    this.execute();
  };

  GTFS.Command.Shape.Revert.prototype.undo = function() {
    this.newShape.hideLegs();
    this.trip.shapes = this.oldShape;
    this.newShape.hideLegs();
    this.oldShape.showLegs();
  };

  GTFS.Command.Stop.InsertBefore = function(shape, stop) {
    this.shape = shape;
    var idx = shape.getSequence(stop);
    if (idx < 0) {
      this.wrong = true;
      return;
    }
    this.afterStop = stop;
    if (idx > 0) {
      this.beforeStop = shape.stops[idx - 1];
      this.oldLeg = stop.preLeg.clone();
    } else {
      this.oldLeg = null;
      this.beforeStop = null;
    }
  };

  GTFS.Command.Stop.InsertBefore.prototype.execute = function() {
    if (this.wrong) return;
    var self = this;
    this.inAsync = true;
    this.shape.insertBefore(this.afterStop, function(stop) {
      self.newStop = stop;
      self.inAsync = false;
    });

  };
  GTFS.Command.Stop.InsertBefore.prototype.undo = function() {
    if (this.wrong) return;
    var self = this;
    this.inAsync = true;
    this.shape.remove(this.newStop, function() {
      if (self.oldLeg) {
        self.beforeStop.sufLeg.setPolyline(self.oldLeg.getPolyline());
        self.beforeStop.sufLeg.show();
      }
      self.inAsync = false;
    });
  };

  GTFS.Command.Stop.InsertBefore.prototype.redo = function() {
    this.execute();
  };

  GTFS.Command.Stop.InsertAfter = function(shape, stop) {
    this.shape = shape;
    var idx = shape.getSequence(stop);
    if (idx < 0) {
      this.wrong = true;
      return;
    }
    this.beforeStop = stop;
    if (idx < shape.stops.length - 1) {
      this.afterStop = shape.stops[idx + 1];
      this.oldLeg = stop.sufLeg.clone();
    } else {
      this.oldLeg = null;
      this.afterStop = null;
    }
  };

  GTFS.Command.Stop.InsertAfter.prototype.execute = function() {
    if (this.wrong) return;
    var self = this;
    this.inAsync = true;
    this.shape.insertAfter(this.beforeStop, function(stop) {
      self.newStop = stop;
      self.inAsync = false;
    });
  };
  GTFS.Command.Stop.InsertAfter.prototype.undo = function() {
    if (this.wrong) return;
    var self = this;
    this.inAsync = true;
    this.shape.remove(this.newStop, function() {
      if (self.oldLeg) {
        self.afterStop.preLeg.setPolyline(self.oldLeg.getPolyline());
        self.afterStop.preLeg.show();
      }
      self.inAsync = false;
    });
  };
  GTFS.Command.Stop.InsertAfter.prototype.redo = function() {
    this.execute();
  };

  GTFS.Command.Stop.Remove = function(shape, stop) {
    var idx = shape.getSequence(stop);
    if (idx < 0) {
      this.wrong = true;
      return;
    }
    this.shape = shape;
    this.stop = stop;

    if (stop.preLeg) {
      this.preLeg = stop.preLeg.clone();
    }
    if (stop.sufLeg) {
      this.sufLeg = stop.sufLeg.clone();
    }

    if (idx < shape.stops.length - 1) {
      this.removeAfter = shape.stops[idx + 1];
    } else {
      this.removeBefore = shape.stops[idx - 1];
    }
    this.sequence = stop.sequence;
    this.idx = idx;
  };

  GTFS.Command.Stop.Remove.prototype.execute = function() {
    if (this.wrong) return;
    var self = this;
    this.inAsync = true;
    this.shape.remove(this.stop, function() {
      self.inAsync = false;
    });
  };

  GTFS.Command.Stop.Remove.prototype.undo = function() {
    if (this.wrong) return;

    var self = this;
    this.inAsync = true;

    this.shape.insertOldStop(this.stop, this.sequence, this.idx, function() {
      if (self.stop.preLeg) {
        self.stop.preLeg.setPolyline(self.preLeg.getPolyline());
        self.stop.preLeg.show();
      }
      if (self.stop.sufLeg) {
        self.stop.sufLeg.setPolyline(self.sufLeg.getPolyline());
        self.stop.sufLeg.show();
      }
      self.stop.showMarker();
      self.inAsync = false;
    });
  };
  GTFS.Command.Stop.Remove.prototype.redo = function() {
    if (this.wrong) return;
    this.execute();
  };

  GTFS.Command.Stop.Copy = function(shape, stop, from, to) {
    this.shape = shape;
    this.stop = stop;
    this.stopId = stop.id;
    this.oldParentStation = stop.parentStation;
    this.newParentStation = null;

    var latlng = this.stop.getLatLng();
    this.latlng = new GLatLng(latlng.lat(), latlng.lng());
    this.image = stop.getImage();
    this.newStop = null;
    var idx = shape.getSequence(stop);
    if (idx < 0) {
      this.wrong = true;
      return;
    }

    if (stop.preLeg) {
      this.preLeg = stop.preLeg.clone();
    }
    if (stop.sufLeg) {
      this.sufLeg = stop.sufLeg.clone();
    }

    if (idx < shape.stops.length - 1) {
      this.removeAfter = shape.stops[idx + 1];
    } else {
      this.removeBefore = shape.stops[idx - 1];
    }
    this.sequence = stop.sequence;
    this.idx = idx;
    this.from = from;
    this.to = to;

  }

  GTFS.Command.Stop.Copy.prototype.undo = function() {
    if (this.wrong) return;
    var self = this;
    this.inAsync = true;
    this.shape.replace(this.stop, this.newStop, function(){
      self.newStop.hideMarker();
      if (self.stop.preLeg) {
        self.stop.preLeg.setPolyline(self.preLeg.getPolyline());
        self.stop.preLeg.show();
      }
      if (self.stop.sufLeg) {
        self.stop.sufLeg.setPolyline(self.sufLeg.getPolyline());
        self.stop.sufLeg.show();
      }
      self.stop.showMarker();
      if(self.from){
        self.stop.setLatLng(self.from);
      }

      self.inAsync = false;
    });
  }

  GTFS.Command.Stop.Copy.prototype.redo = function() {
    if (this.wrong) return;
    var self = this;
    var stop = this.stop;
    this.inAsync = true;
    this.shape.replace(this.newStop, this.stop, function(){
      self.inAsync = false;
      self.newStop.showMarker();
      self.stop.hideMarker();
      if (self.newStop.preLeg) {
        self.newStop.preLeg.setPolyline(self.preVerifyLeg.getPolyline());
        self.newStop.preLeg.show();
      }
      if (self.newStop.sufLeg) {
        self.newStop.sufLeg.setPolyline(self.sufVerifyLeg.getPolyline());
        self.newStop.sufLeg.show();
      }
      self.newStop.showMarker();
      if(self.to){
        self.newStop.setLatLng(self.to);
      }
    });
  }

  GTFS.Command.Stop.Copy.prototype.execute = function() {
    if (this.wrong) return;

    var self = this;
    var stop = this.stop;
    this.inAsync = true;
    this.shape.copy(stop, function(station){
      self.newStop = station;
      if(self.to) {
        self.newStop.setLatLng(self.to)
      }
    if (station.preLeg) {
      this.preVerifyLeg = station.preLeg.clone();
    }
    if (station.sufLeg) {
      this.sufVerifyLeg = station.sufLeg.clone();
    }

      self.inAsync = false;
    });
  }

  GTFS.History = function() {
    this.redoStack = [];
    this.undoStack = [];
  };

  GTFS.History.prototype.execute = function(cmd) {
    this.redoStack = [];
    cmd.execute();
    this.undoStack.push(cmd);
  }

  GTFS.History.prototype.redo = function() {
    var cmd = this.redoStack.pop();

    if (cmd) {
      if (cmd.inAsync) {
        this.redoStack.push(cmd);
        return;
      }
      cmd.redo();
      this.undoStack.push(cmd);
    }
  };

  GTFS.History.prototype.undo = function() {
    var cmd = this.undoStack.pop();

    if (cmd) {
      if (cmd.inAsync) {
        this.undoStack.push(cmd);
        return;
      }
      cmd.undo();
      this.redoStack.push(cmd);
    }
  };

  // dom events initializing
  GEvent.addDomListener(window, 'load', function() {
    load(function() {
      var manager = new GTFS.Data.Manager();
      manager.initIcon();
      manager.getAgency(function() {
        manager.showAgency();
      });

      GEvent.addListener(map, 'mousemove', function(point) {
        GTFS.mousePoint = point;
      });

      GEvent.addDomListener($('editSave'), 'click', saveData);
      GEvent.addDomListener($('stopTextSearchButton'), 'click', stopTextSearchSubmit);
      GEvent.addDomListener($('tripTextSearchButton'), 'click', tripTextSearchSubmit);
    });
  });
  GEvent.addDomListener(window, 'resize', sizeRouteList);
  GEvent.addDomListener(window, 'unload', GUnload);
})();

